예제 #1
0
    def my_constructor(
        self,
        PARENT_NODE=None,
        SCENE_ROOT=None,
        TARGET_LIST=[],
    ):

        ### external references ###
        self.SCENE_ROOT = SCENE_ROOT
        self.TARGET_LIST = TARGET_LIST

        ### variables ###
        self.dragging_technique = 0
        self.dragged_objects_list = []
        self.lf_hand_mat = avango.gua.make_identity_mat(
        )  # last frame hand matrix

        ## init hand geometry
        _loader = avango.gua.nodes.TriMeshLoader(
        )  # init trimesh loader to load external meshes

        self.hand_geometry = _loader.create_geometry_from_file(
            "hand_geometry", "data/objects/hand.obj",
            avango.gua.LoaderFlags.DEFAULTS)
        self.hand_geometry.Transform.value = \
            avango.gua.make_rot_mat(45.0,1,0,0) * \
            avango.gua.make_rot_mat(180.0,0,1,0) * \
            avango.gua.make_scale_mat(0.06)
        self.hand_geometry.Material.value.set_uniform(
            "Color", avango.gua.Vec4(1.0, 0.86, 0.54, 1.0))
        self.hand_geometry.Material.value.set_uniform("Emissivity", 0.9)
        self.hand_geometry.Material.value.set_uniform("Metalness", 0.1)

        self.hand_transform = avango.gua.nodes.TransformNode(
            Name="hand_transform")
        self.hand_transform.Children.value = [self.hand_geometry]
        PARENT_NODE.Children.value.append(self.hand_transform)
        self.hand_transform.Transform.connect_from(self.sf_hand_mat)

        ### init sub-classes ###
        self.mouseInput = MouseInput()
        self.mouseInput.my_constructor("gua-device-mouse")

        # init manipulation technique
        self.IPCManipulation = IsotonicPositionControlManipulation()
        self.IPCManipulation.my_constructor(self.mouseInput.mf_dof,
                                            self.mouseInput.mf_buttons)
        self.IPCManipulation.enable_manipulation(True)

        # init field connections
        self.sf_hand_mat.connect_from(self.IPCManipulation.sf_mat)
        self.sf_dragging_trigger.connect_from(
            self.IPCManipulation.sf_action_trigger)

        ## init keyboard sensor
        self.keyboard_sensor = avango.daemon.nodes.DeviceSensor(
            DeviceService=avango.daemon.DeviceService())
        self.keyboard_sensor.Station.value = "gua-device-keyboard0"

        self.sf_key_1.connect_from(self.keyboard_sensor.Button16)  # key 1
        self.sf_key_2.connect_from(self.keyboard_sensor.Button17)  # key 2
        self.sf_key_3.connect_from(self.keyboard_sensor.Button18)  # key 3

        ### set initial states ###
        self.set_dragging_technique(1)  # switch to 1st dragging variant
예제 #2
0
    def my_constructor(self,
        PARENT_NODE = None,
        SCENE_ROOT = None,
        TARGET_LIST = [],
        ):
        

        ### external references ###        
        self.SCENE_ROOT = SCENE_ROOT
        self.TARGET_LIST = TARGET_LIST


        ### variables ###
        self.dragged_objects_list = []
        self.lf_hand_mat = avango.gua.make_identity_mat() # last frame hand matrix

        
        ## init hand geometry
        _loader = avango.gua.nodes.TriMeshLoader() # init trimesh loader to load external meshes
        
        self.hand_geometry = _loader.create_geometry_from_file("hand_geometry", "data/objects/hand.obj", avango.gua.LoaderFlags.DEFAULTS)
        self.hand_geometry.Transform.value = \
            avango.gua.make_rot_mat(45.0,1,0,0) * \
            avango.gua.make_rot_mat(180.0,0,1,0) * \
            avango.gua.make_scale_mat(0.06)
        self.hand_geometry.Material.value.set_uniform("Color", avango.gua.Vec4(1.0, 0.86, 0.54, 1.0))
        self.hand_geometry.Material.value.set_uniform("Emissivity", 0.9)
        self.hand_geometry.Material.value.set_uniform("Metalness", 0.1)  
        
        self.hand_transform = avango.gua.nodes.TransformNode(Name = "hand_transform")
        self.hand_transform.Children.value = [self.hand_geometry]
        PARENT_NODE.Children.value.append(self.hand_transform)
        self.hand_transform.Transform.connect_from(self.sf_hand_mat)
        

        ### init sub-classes ###
        
        ## init inputs
        self.mouseInput = MouseInput()
        self.mouseInput.my_constructor("gua-device-mouse")

        if SPACEMOUSE_TYPE == "Spacemouse":
            self.spacemouseInput = SpacemouseInput()
            self.spacemouseInput.my_constructor("gua-device-spacemouse")

        elif SPACEMOUSE_TYPE == "Blue Spacemouse":
            self.spacemouseInput = NewSpacemouseInput()
            self.spacemouseInput.my_constructor("gua-device-spacemouse")
        

        ## init manipulation techniques
        self.IPCManipulation = IsotonicPositionControlManipulation()
        self.IPCManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.EPCManipulation = ElasticPositionControlManipulation()
        self.EPCManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)

        self.IRCManipulation = IsotonicRateControlManipulation()
        self.IRCManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.ERCManipulation = ElasticRateControlManipulation()
        self.ERCManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)

        self.IACManipulation = IsotonicAccelerationControlManipulation()
        self.IACManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.EACManipulation = ElasticAccelerationControlManipulation()
        self.EACManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)
        
        self.NIIPCManipulation = NonIsomorphicIsotonicPositionControlManipulation()
        self.NIIPCManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.NIERCManipulation = NonIsomorphicElasticRateControlManipulation()
        self.NIERCManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)


        ## init keyboard sensor for system control
        self.keyboard_sensor = avango.daemon.nodes.DeviceSensor(DeviceService = avango.daemon.DeviceService())
        self.keyboard_sensor.Station.value = "gua-device-keyboard0"

        self.sf_key_1.connect_from(self.keyboard_sensor.Button16) # key 1
        self.sf_key_2.connect_from(self.keyboard_sensor.Button17) # key 2
        self.sf_key_3.connect_from(self.keyboard_sensor.Button18) # key 3
        self.sf_key_4.connect_from(self.keyboard_sensor.Button19) # key 4
        self.sf_key_5.connect_from(self.keyboard_sensor.Button20) # key 5
        self.sf_key_6.connect_from(self.keyboard_sensor.Button21) # key 6
        self.sf_key_7.connect_from(self.keyboard_sensor.Button22) # key 7
        self.sf_key_8.connect_from(self.keyboard_sensor.Button23) # key 8


        ### set initial states ###
        self.set_manipulation_technique(1) # switch to isotonic position control
예제 #3
0
class ManipulationManager(avango.script.Script):

    ### input fields
    sf_key_1 = avango.SFBool()
    sf_key_2 = avango.SFBool()
    sf_key_3 = avango.SFBool()

    ### internal fields
    sf_hand_mat = avango.gua.SFMatrix4()
    sf_dragging_trigger = avango.SFBool()

    # constructor
    def __init__(self):
        self.super(ManipulationManager).__init__()

    def my_constructor(
        self,
        PARENT_NODE=None,
        SCENE_ROOT=None,
        TARGET_LIST=[],
    ):

        ### external references ###
        self.SCENE_ROOT = SCENE_ROOT
        self.TARGET_LIST = TARGET_LIST

        ### variables ###
        self.dragging_technique = 0
        self.dragged_objects_list = []
        self.lf_hand_mat = avango.gua.make_identity_mat(
        )  # last frame hand matrix

        ## init hand geometry
        _loader = avango.gua.nodes.TriMeshLoader(
        )  # init trimesh loader to load external meshes

        self.hand_geometry = _loader.create_geometry_from_file(
            "hand_geometry", "data/objects/hand.obj",
            avango.gua.LoaderFlags.DEFAULTS)
        self.hand_geometry.Transform.value = \
            avango.gua.make_rot_mat(45.0,1,0,0) * \
            avango.gua.make_rot_mat(180.0,0,1,0) * \
            avango.gua.make_scale_mat(0.06)
        self.hand_geometry.Material.value.set_uniform(
            "Color", avango.gua.Vec4(1.0, 0.86, 0.54, 1.0))
        self.hand_geometry.Material.value.set_uniform("Emissivity", 0.9)
        self.hand_geometry.Material.value.set_uniform("Metalness", 0.1)

        self.hand_transform = avango.gua.nodes.TransformNode(
            Name="hand_transform")
        self.hand_transform.Children.value = [self.hand_geometry]
        PARENT_NODE.Children.value.append(self.hand_transform)
        self.hand_transform.Transform.connect_from(self.sf_hand_mat)

        ### init sub-classes ###
        self.mouseInput = MouseInput()
        self.mouseInput.my_constructor("gua-device-mouse")

        # init manipulation technique
        self.IPCManipulation = IsotonicPositionControlManipulation()
        self.IPCManipulation.my_constructor(self.mouseInput.mf_dof,
                                            self.mouseInput.mf_buttons)
        self.IPCManipulation.enable_manipulation(True)

        # init field connections
        self.sf_hand_mat.connect_from(self.IPCManipulation.sf_mat)
        self.sf_dragging_trigger.connect_from(
            self.IPCManipulation.sf_action_trigger)

        ## init keyboard sensor
        self.keyboard_sensor = avango.daemon.nodes.DeviceSensor(
            DeviceService=avango.daemon.DeviceService())
        self.keyboard_sensor.Station.value = "gua-device-keyboard0"

        self.sf_key_1.connect_from(self.keyboard_sensor.Button16)  # key 1
        self.sf_key_2.connect_from(self.keyboard_sensor.Button17)  # key 2
        self.sf_key_3.connect_from(self.keyboard_sensor.Button18)  # key 3

        ### set initial states ###
        self.set_dragging_technique(1)  # switch to 1st dragging variant

    ### functions ###

    def set_dragging_technique(self, INT):
        self.dragging_technique = INT

        print("Dragging Technique set to technique", self.dragging_technique)

    def start_dragging(self):
        _hand_mat = self.hand_transform.Transform.value

        for _node in self.TARGET_LIST:
            if self.is_highlight_material(
                    _node.CurrentColor.value
            ) == True:  # a monkey node in close proximity
                _node.CurrentColor.value = avango.gua.Vec4(1.0, 0.0, 0.0, 1.0)
                _node.Material.value.set_uniform(
                    "Color",
                    _node.CurrentColor.value)  # switch to dragging material

                self.dragged_objects_list.append(
                    _node)  # add node for dragging

                ## TODO: Implement individual components of the different dragging strategies here ##
                if self.dragging_technique == 1:  # change of node order in scenegraph
                    _node.Parent.value.Children.value.remove(_node)
                    self.hand_transform.Children.value.append(_node)
                    _node.Transform.value = avango.gua.make_inverse_mat(
                        self.hand_transform.WorldTransform.value
                    ) * _node.Transform.value
                    pass

                elif self.dragging_technique == 2:  # absolute tool-hand offset to tool space
                    _node.DraggingOffsetMatrix.value = avango.gua.make_inverse_mat(
                        self.hand_transform.WorldTransform.value
                    ) * _node.Transform.value
                    pass

                elif self.dragging_technique == 3:  # relative tool input to object space
                    pass

    def update_dragging_candidates(self):
        #_hand_pos = self.hand_transform.Transform.value.get_translate()
        _hand_pos = self.hand_transform.WorldTransform.value.get_translate(
        )  #without WorldTransform when changing viewport the detection of correct monkey dist breaks

        for _node in self.TARGET_LIST:
            #_pos = _node.Transform.value.get_translate() # a monkey position
            _pos = _node.WorldTransform.value.get_translate(
            )  # a monkey position

            _dist = (_hand_pos - _pos).length()  # hand-object distance
            _color = _node.CurrentColor.value

            ## toggle object highlight
            if _dist < 0.025 and self.is_default_material(_color) == True:
                _node.CurrentColor.value = avango.gua.Vec4(0.0, 1.0, 0.0, 1.0)
                _node.Material.value.set_uniform(
                    "Color",
                    _node.CurrentColor.value)  # switch to highlight material

            elif _dist > 0.03 and self.is_highlight_material(_color) == True:
                _node.CurrentColor.value = avango.gua.Vec4(1.0, 1.0, 1.0, 1.0)
                _node.Material.value.set_uniform(
                    "Color",
                    _node.CurrentColor.value)  # switch to default material

    def object_dragging(self):
        # apply hand movement to (all) dragged objects
        for _node in self.dragged_objects_list:
            ## TODO: Implement individual components of the different dragging strategies here ##
            if self.dragging_technique == 1:  # change node order in scenegraph

                pass

            elif self.dragging_technique == 2:  # absolute tool-hand offset to tool space
                _node.Transform.value = self.hand_transform.WorldTransform.value * _node.DraggingOffsetMatrix.value
                pass

            elif self.dragging_technique == 3:  # relative tool input to object space
                self.diff_hand_mat = avango.gua.make_inverse_mat(
                    self.lf_hand_mat
                ) * self.hand_transform.WorldTransform.value
                _node.Transform.value = self.diff_hand_mat * _node.Transform.value
                pass

    def stop_dragging(self):
        ## handle all dragged objects
        for _node in self.dragged_objects_list:
            _node.CurrentColor.value = avango.gua.Vec4(0.0, 1.0, 0.0, 1.0)
            _node.Material.value.set_uniform(
                "Color",
                _node.CurrentColor.value)  # switch to highlight material

        ## TODO: Implement individual components of the different dragging strategies here ##
        if self.dragging_technique == 1:  # change node order in scenegraph
            transform = _node.WorldTransform.value
            _node.Parent.value.Children.value.remove(_node)
            self.SCENE_ROOT.Children.value.append(_node)
            _node.Transform.value = transform

            pass

        elif self.dragging_technique == 2:  # absolute tool-hand offset to tool space
            pass

        elif self.dragging_technique == 3:  # relative tool input to object space
            pass

        self.dragged_objects_list = []  # clear list

    def is_default_material(self, VEC4):
        return VEC4.x == 1.0 and VEC4.y == 1.0 and VEC4.z == 1.0 and VEC4.w == 1.0

    def is_highlight_material(self, VEC4):
        return VEC4.x == 0.0 and VEC4.y == 1.0 and VEC4.z == 0.0 and VEC4.w == 1.0

    def is_dragging_material(self, VEC4):
        return VEC4.x == 1.0 and VEC4.y == 0.0 and VEC4.z == 0.0 and VEC4.w == 1.0

    ### callback functions ###

    @field_has_changed(sf_key_1)
    def sf_key_1_changed(self):
        if self.sf_key_1.value == True:  # key is pressed
            self.set_dragging_technique(1)  # switch dragging technique

    @field_has_changed(sf_key_2)
    def sf_key_2_changed(self):
        if self.sf_key_2.value == True:  # key is pressed
            self.set_dragging_technique(2)  # switch dragging technique

    @field_has_changed(sf_key_3)
    def sf_key_3_changed(self):
        if self.sf_key_3.value == True:  # key is pressed
            self.set_dragging_technique(3)  # switch dragging technique

    @field_has_changed(sf_dragging_trigger)
    def sf_dragging_trigger_changed(self):
        if self.sf_dragging_trigger.value == True:
            self.start_dragging()
        else:
            self.stop_dragging()

    def evaluate(
        self
    ):  # evaluated every frame if any input field has changed (incl. dependency evaluation)
        self.update_dragging_candidates()

        self.object_dragging()  # possibly drag object with hand input

        self.lf_hand_mat = self.hand_transform.WorldTransform.value
예제 #4
0
class ManipulationManager(avango.script.Script):

    ### input fields
    sf_key_1 = avango.SFBool()
    sf_key_2 = avango.SFBool()
    sf_key_3 = avango.SFBool()
    sf_key_4 = avango.SFBool()
    sf_key_5 = avango.SFBool()
    sf_key_6 = avango.SFBool()
    sf_key_7 = avango.SFBool()
    sf_key_8 = avango.SFBool()          

    sf_hand_mat = avango.gua.SFMatrix4()
    sf_dragging_trigger = avango.SFBool()


    # constructor
    def __init__(self):
        self.super(ManipulationManager).__init__()
    

    def my_constructor(self,
        PARENT_NODE = None,
        SCENE_ROOT = None,
        TARGET_LIST = [],
        ):
        

        ### external references ###        
        self.SCENE_ROOT = SCENE_ROOT
        self.TARGET_LIST = TARGET_LIST


        ### variables ###
        self.dragged_objects_list = []
        self.lf_hand_mat = avango.gua.make_identity_mat() # last frame hand matrix

        
        ## init hand geometry
        _loader = avango.gua.nodes.TriMeshLoader() # init trimesh loader to load external meshes
        
        self.hand_geometry = _loader.create_geometry_from_file("hand_geometry", "data/objects/hand.obj", avango.gua.LoaderFlags.DEFAULTS)
        self.hand_geometry.Transform.value = \
            avango.gua.make_rot_mat(45.0,1,0,0) * \
            avango.gua.make_rot_mat(180.0,0,1,0) * \
            avango.gua.make_scale_mat(0.06)
        self.hand_geometry.Material.value.set_uniform("Color", avango.gua.Vec4(1.0, 0.86, 0.54, 1.0))
        self.hand_geometry.Material.value.set_uniform("Emissivity", 0.9)
        self.hand_geometry.Material.value.set_uniform("Metalness", 0.1)  
        
        self.hand_transform = avango.gua.nodes.TransformNode(Name = "hand_transform")
        self.hand_transform.Children.value = [self.hand_geometry]
        PARENT_NODE.Children.value.append(self.hand_transform)
        self.hand_transform.Transform.connect_from(self.sf_hand_mat)
        

        ### init sub-classes ###
        
        ## init inputs
        self.mouseInput = MouseInput()
        self.mouseInput.my_constructor("gua-device-mouse")

        if SPACEMOUSE_TYPE == "Spacemouse":
            self.spacemouseInput = SpacemouseInput()
            self.spacemouseInput.my_constructor("gua-device-spacemouse")

        elif SPACEMOUSE_TYPE == "Blue Spacemouse":
            self.spacemouseInput = NewSpacemouseInput()
            self.spacemouseInput.my_constructor("gua-device-spacemouse")
        

        ## init manipulation techniques
        self.IPCManipulation = IsotonicPositionControlManipulation()
        self.IPCManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.EPCManipulation = ElasticPositionControlManipulation()
        self.EPCManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)

        self.IRCManipulation = IsotonicRateControlManipulation()
        self.IRCManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.ERCManipulation = ElasticRateControlManipulation()
        self.ERCManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)

        self.IACManipulation = IsotonicAccelerationControlManipulation()
        self.IACManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.EACManipulation = ElasticAccelerationControlManipulation()
        self.EACManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)
        
        self.NIIPCManipulation = NonIsomorphicIsotonicPositionControlManipulation()
        self.NIIPCManipulation.my_constructor(self.mouseInput.mf_dof, self.mouseInput.mf_buttons)

        self.NIERCManipulation = NonIsomorphicElasticRateControlManipulation()
        self.NIERCManipulation.my_constructor(self.spacemouseInput.mf_dof, self.spacemouseInput.mf_buttons)


        ## init keyboard sensor for system control
        self.keyboard_sensor = avango.daemon.nodes.DeviceSensor(DeviceService = avango.daemon.DeviceService())
        self.keyboard_sensor.Station.value = "gua-device-keyboard0"

        self.sf_key_1.connect_from(self.keyboard_sensor.Button16) # key 1
        self.sf_key_2.connect_from(self.keyboard_sensor.Button17) # key 2
        self.sf_key_3.connect_from(self.keyboard_sensor.Button18) # key 3
        self.sf_key_4.connect_from(self.keyboard_sensor.Button19) # key 4
        self.sf_key_5.connect_from(self.keyboard_sensor.Button20) # key 5
        self.sf_key_6.connect_from(self.keyboard_sensor.Button21) # key 6
        self.sf_key_7.connect_from(self.keyboard_sensor.Button22) # key 7
        self.sf_key_8.connect_from(self.keyboard_sensor.Button23) # key 8


        ### set initial states ###
        self.set_manipulation_technique(1) # switch to isotonic position control


    ### functions ###
    def set_manipulation_technique(self, INT):
        self.manipulation_technique = INT

        # disable prior manipulation technique
        self.IPCManipulation.enable_manipulation(False)
        self.EPCManipulation.enable_manipulation(False)
        self.IRCManipulation.enable_manipulation(False)
        self.ERCManipulation.enable_manipulation(False)
        self.IACManipulation.enable_manipulation(False)      
        self.EACManipulation.enable_manipulation(False)
        self.NIIPCManipulation.enable_manipulation(False)
        self.NIERCManipulation.enable_manipulation(False)
        
        # remove existing field connections    
        self.sf_hand_mat.disconnect()
        self.sf_dragging_trigger.disconnect()
        
        if self.manipulation_technique == 1: # isotonic position control     
            self.IPCManipulation.enable_manipulation(True)

            # init field connections      
            self.sf_hand_mat.connect_from(self.IPCManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.IPCManipulation.sf_action_trigger)
        
        elif self.manipulation_technique == 2: # elastic position control        
            self.EPCManipulation.enable_manipulation(True)

            # init field connections
            self.sf_hand_mat.connect_from(self.EPCManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.EPCManipulation.sf_action_trigger)            
        
        elif self.manipulation_technique == 3: # isotonic rate control        
            self.IRCManipulation.enable_manipulation(True)
        
            # init field connections
            self.sf_hand_mat.connect_from(self.IRCManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.IRCManipulation.sf_action_trigger)
        
        elif self.manipulation_technique == 4: # elastic rate control
            self.ERCManipulation.enable_manipulation(True)

            # init field connections      
            self.sf_hand_mat.connect_from(self.ERCManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.ERCManipulation.sf_action_trigger)
        
        elif self.manipulation_technique == 5: # isotonic acceleration control
            self.IACManipulation.enable_manipulation(True)

            # init field connections      
            self.sf_hand_mat.connect_from(self.IACManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.IACManipulation.sf_action_trigger)

        elif self.manipulation_technique == 6: # elastic acceleration control        
            self.EACManipulation.enable_manipulation(True)

            # init field connections      
            self.sf_hand_mat.connect_from(self.EACManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.EACManipulation.sf_action_trigger)

        elif self.manipulation_technique == 7:
            self.NIIPCManipulation.enable_manipulation(True)

            # init field connections      
            self.sf_hand_mat.connect_from(self.NIIPCManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.NIIPCManipulation.sf_action_trigger)

        elif self.manipulation_technique == 8:
            self.NIERCManipulation.enable_manipulation(True)

            # init field connections      
            self.sf_hand_mat.connect_from(self.NIERCManipulation.sf_mat)
            self.sf_dragging_trigger.connect_from(self.NIERCManipulation.sf_action_trigger)


    def start_dragging(self):  
        _hand_mat = self.hand_transform.WorldTransform.value

        for _node in self.TARGET_LIST:
            if self.is_highlight_material(_node.CurrentColor.value) == True: # a monkey node in close proximity
                _node.CurrentColor.value = avango.gua.Vec4(1.0, 0.0, 0.0, 1.0)
                _node.Material.value.set_uniform("Color", _node.CurrentColor.value) # switch to dragging material

                self.dragged_objects_list.append(_node) # add node for dragging
          
                ## dragging without snapping

                # clac tool-hand offset
                _dragging_offset_mat = avango.gua.make_inverse_mat(_hand_mat) * _node.Transform.value # object transformation in hand coordinate system
                _node.DraggingOffsetMatrix.value = _dragging_offset_mat # here you can store node dependent dragging transformations 

      
    def update_dragging_candidates(self):
        _hand_pos = self.hand_transform.WorldTransform.value.get_translate()
    
        for _node in self.TARGET_LIST:
            _pos = _node.Transform.value.get_translate() # a monkey position

            _dist = (_hand_pos - _pos).length() # hand-object distance
            _color = _node.CurrentColor.value

            ## toggle object highlight
            if _dist < 0.025 and self.is_default_material(_color) == True:
                _node.CurrentColor.value = avango.gua.Vec4(0.0, 1.0, 0.0, 1.0)
                _node.Material.value.set_uniform("Color", _node.CurrentColor.value) # switch to highlight material

            elif _dist > 0.03 and self.is_highlight_material(_color) == True:
                _node.CurrentColor.value = avango.gua.Vec4(1.0, 1.0, 1.0, 1.0)
                _node.Material.value.set_uniform("Color", _node.CurrentColor.value) # switch to default material
    

    def object_dragging(self):
        # apply hand movement to (all) dragged objects
        for _node in self.dragged_objects_list:
            _node.Transform.value = self.hand_transform.WorldTransform.value * _node.DraggingOffsetMatrix.value # apply tool-hand offset to absolute hand transformation            

  
    def stop_dragging(self):  
        ## handle all dragged objects
        for _node in self.dragged_objects_list:      
            _node.CurrentColor.value = avango.gua.Vec4(0.0, 1.0, 0.0, 1.0)
            _node.Material.value.set_uniform("Color", _node.CurrentColor.value) # switch to highlight material
    
        self.dragged_objects_list = [] # clear list


    def is_default_material(self, VEC4):
        return VEC4.x == 1.0 and VEC4.y == 1.0 and VEC4.z == 1.0 and VEC4.w == 1.0


    def is_highlight_material(self, VEC4):
        return VEC4.x == 0.0 and VEC4.y == 1.0 and VEC4.z == 0.0 and VEC4.w == 1.0


    def is_dragging_material(self, VEC4):
        return VEC4.x == 1.0 and VEC4.y == 0.0 and VEC4.z == 0.0 and VEC4.w == 1.0
      
    
    ### callback functions ###

    @field_has_changed(sf_key_1)
    def sf_key_1_changed(self):
        if self.sf_key_1.value == True: # key is pressed
            self.set_manipulation_technique(1) # switch to isotonic position control
           

    @field_has_changed(sf_key_2)
    def sf_key_2_changed(self):
        if self.sf_key_2.value == True: # key is pressed
            self.set_manipulation_technique(2) # switch to elastic position control


    @field_has_changed(sf_key_3)
    def sf_key_3_changed(self):
        if self.sf_key_3.value == True: # key is pressed
            self.set_manipulation_technique(3) # switch to isotonic rate control


    @field_has_changed(sf_key_4)
    def sf_key_4_changed(self):
        if self.sf_key_4.value == True: # key is pressed
            self.set_manipulation_technique(4) # switch to elastic rate control
      

    @field_has_changed(sf_key_5)
    def sf_key_5_changed(self):
        if self.sf_key_5.value == True: # key is pressed
            self.set_manipulation_technique(5) # switch to isotonic acceleration control


    @field_has_changed(sf_key_6)
    def sf_key_6_changed(self):
        if self.sf_key_6.value == True: # key is pressed
            self.set_manipulation_technique(6) # switch to elastic acceleration control

    @field_has_changed(sf_key_7)
    def sf_key_7_changed(self):
        print("field_has_changed")
        if self.sf_key_7.value == True: # key is pressed
            self.set_manipulation_technique(7) # switch to elastic acceleration control

    @field_has_changed(sf_key_8)
    def sf_key_8_changed(self):
        if self.sf_key_8.value == True: # key is pressed
            self.set_manipulation_technique(8) # switch to elastic acceleration control


    @field_has_changed(sf_dragging_trigger)
    def sf_dragging_trigger_changed(self):
        if self.sf_dragging_trigger.value == True:
            self.start_dragging()  
        else:
            self.stop_dragging()
     

    def evaluate(self): # evaluated every frame if any input field has changed (incl. dependency evaluation)
        self.update_dragging_candidates()

        self.object_dragging() # possibly drag object with hand input


        ## print covered distance and hand velocity as debug output
        _distance = (self.sf_hand_mat.value.get_translate() - self.lf_hand_mat.get_translate()).length()
        _velocity = _distance * 60.0 # application loop runs with 60Hz
        self.lf_hand_mat = self.sf_hand_mat.value