1
votes

Ok, I'm just confused (and probably too tired for my own good to be working on this now...). I'm trying to get my three.js application to execute different functions for different mouse events when the mouse is over a particular type of entity in my scene. Events of type "mousemove" are working perfectly, and the

console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', mouse.type );

statement registers all the mouseEvents that I'm listening for, which include "mousemove", "click", "dblclick", "wheel" and "oncontextmenu". It's also detecting INTERSECTED.isGraphElement objects as intented.

Nevertheless, when transformGraphElement() runs, only the "mousemove" events are registered inside the transformGraphElement function. Even the commented-out test line of code that should console out "Got the dblclick!" doesn't run. It appears that no other mouse events that I'm listening for are being detected here.

Things I've tried inside the transformGraphElement function:

  • Swap out the dblclick event for one of the other types I'm listening for. No dice.
  • Comment out the line for handling mousemove events. No dice.
  • Test to see if there's a bug in my obj.referent.transformOnDblClick() function. I only learned that it wasn't being called at all (and runs perfectly when I associate it to the "mousemove" event).
  • Trying different valid syntaxes for my 'if' statements. No dice.
  • Trying a bit of refactoring on the mouseEventHandler() function. No dice.

Here's my relevant code:


    function render() {

        mouseEventHandler( transformGraphElement, unTransformGraphElement );

        requestAnimationFrame( render );
        renderer.render(scene, entities.cameras.perspCamera );
    }

    function mouseEventHandler( fn, revFn ){

        // update the picking ray with the camera and mouse position
        ray.setFromCamera( mouse, entities.cameras.perspCamera );

        // calculate objects intersecting the picking ray
        var intersects = ray.intersectObjects( scene.children );

        if ( intersects && intersects[0] && intersects[0].object ){

            if ( intersects[ 0 ].object != INTERSECTED ){   // if there's an intersected object

                if ( INTERSECTED ) {                        // and if a previous INTERSECTED object exists:
                    revFn( INTERSECTED, mouse );                    // restore the previous intersected object to its non-intersected state.                
                }                       

                INTERSECTED = intersects[ 0 ].object;       // set the currently intersected object to INTERSECTED
                fn( INTERSECTED, mouse );                           // transform the currentlY INTERSECTED object.

                }   

                console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', mouse.type );
            }
    }

    function transformGraphElement( obj, mouse ){

        // Check if INTERSECTED is a Graph Element, and if so, invoke it's transform function.  
        if ( mouse.type === "mousemove" && obj.isGraphElement ) { obj.referent.transformOnMouseOver(); }
        //if ( mouse.type === 'dblclick' ) { console.log('Got the dblclick Inside!') }  
        if ( mouse.type === 'dblclick' && obj.isGraphElement ) { obj.referent.transformOnDblClick(); ) }
    }

    function unTransformGraphElement( obj, mouse ){

        // Check if INTERSECTED is a Graph Element, and if so, revert it to it's pre-mouseEvent state.
        if ( mouse.type === "mousemove" && obj.isGraphElement ) { obj.referent.transformOnMouseOut(); }
        if ( mouse.type === 'dblclick' ) { console.log('Got the dblclick Out!') }
    }

I'm wondering if its some sort of default behavior or override that I'm running into, but shouldn't the event.preventDefault() line be handling that? (This code below runs before the code above):


    var ray = new THREE.Raycaster();
    var mouse = new THREE.Vector2();
    var INTERSECTED;  // Object closest to the camera

    function onMouse( event ) {

        event.preventDefault();

        // calculate mouse position in normalized device coordinates
        // (-1 to +1) for both components

        mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
        mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
        mouse.type = ( event.type );

    }

    function listenFor(){
        document.addEventListener( 'click', onMouse, false );
        document.addEventListener( 'mousemove', onMouse, false );
        document.addEventListener( 'mousedown', onMouse, false );
        document.addEventListener( 'dblclick', onMouse, false )
        document.addEventListener( 'wheel', onMouse, false );
        document.addEventListener( 'contextmenu', onMouse, false );
    }

    listenFor();

Console.log( mouse.type ) from inside the onMouse() function registers all the mouse events I'm listening for perfectly.

I've been banging my head against this for 3 hours. I'm hoping it's something stupid simple that I'm missing because I'm in a bad mood. All help is welcome, and let me know if there's any missing code important to providing a useful answer... I don't think so but again, in a bad mood...

Thanks for your help!

1

1 Answers

1
votes

Well the next morning I got up and figured out the problem. The issue was with the logic in the code:

            if ( intersects[ 0 ].object != INTERSECTED ){   // if there's an intersected object

                if ( INTERSECTED ) {                        // and if a previous INTERSECTED object exists:
                    revFn( INTERSECTED, mouse );                    // restore the previous intersected object to its non-intersected state.                
                }                       

                INTERSECTED = intersects[ 0 ].object;       // set the currently intersected object to INTERSECTED
                fn( INTERSECTED, mouse );                           // transform the currentlY INTERSECTED object.

                }   

                console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', mouse.type );
            }

Non-mousemove events could not be passed. My solution was to wrap this section with another conditional if (mouse.type === 'mousemove') and then have additional conditionals for other event types. Here's the whole, with a bit of refactoring to make the whole easier to reason about:

var ray = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var INTERSECTED;  // Object closest to the camera

function onMouse( event ) {

    event.preventDefault();

    // calculate mouse position in normalized device coordinates
    // (-1 to +1) for both components

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    mouseEventHandler( event /*, transformGraphElement, unTransformGraphElement */ );

}

function listenFor(){
    document.addEventListener( 'click', onMouse, false );
    document.addEventListener( 'mousemove', onMouse, false );
    document.addEventListener( 'mousedown', onMouse, false );
    document.addEventListener( 'dblclick', onMouse, false )
    document.addEventListener( 'wheel', onMouse, false );
    document.addEventListener( 'contextmenu', onMouse, false );
}

listenFor();

/* ... */

function render() {

    requestAnimationFrame( render );
    renderer.render(scene, entities.cameras.perspCamera );
}

function mouseEventHandler( event /* , fn, revFn */ ){

    // update the picking ray with the camera and mouse position
    ray.setFromCamera( mouse, entities.cameras.perspCamera );

    // calculate objects intersecting the picking ray
    var intersects = ray.intersectObjects( scene.children );

    // if there's at least one intersected object...
    if ( intersects && intersects[0] && intersects[0].object ){

        // Check if the event is a mouse move, INTERSECTED exists and we're sitting on the same INTERSECTED object as the last time this function ran...        
        if ( event.type === 'mousemove' ){
            // Check if the current top-level intersected object is the previous INTERSECTED        
            if ( intersects[ 0 ].object != INTERSECTED ){
                // ... if there is a previous INTERSECTED
                if ( INTERSECTED ) {    
                    // restore the previous INTERSECTED to it's previous state.
                    unTransformGraphElementOnMouseOut( INTERSECTED, event );                                    
                }                       
                // set the currently intersected object to INTERSECTED  
                INTERSECTED = intersects[ 0 ].object;       
                // and transform it accordingly.
                transformGraphElementOnMouseOver( INTERSECTED, event );                         
                }   
        }

        // Check if the mouse event is a doubble click 
        if ( event.type === 'dblclick' ){
            // If the currently intersected object is INTERSECTED
            if ( intersects[ 0 ].object === INTERSECTED ){
                // select it.               
                transformGraphElementOnSelect( INTERSECTED, event );                            
            }
            // If the currently intersected object is not INTERSECTED
            if ( intersects[ 0 ].object !== INTERSECTED ){
                // If there is a previous INTERSECTED
                if ( INTERSECTED )
                    // restore it to its unselected state.
                    unTransformGraphElementOnUnselect( INTERSECTED, event );                                
            }
        }       

    INTERSECTED && console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', event.type );           
    }
}

function transformGraphElementOnMouseOver( obj, event ){
    if ( obj.isGraphElement ) { obj.referent.transformOnMouseOver(); }  
}

function unTransformGraphElementOnMouseOut( obj, event ){
    if ( obj.isGraphElement ) { obj.referent.transformOnMouseOut(); }
}

function transformGraphElementOnSelect( obj, event ){
    if ( obj.isGraphElement ) { obj.referent.transformOnDblClick(); }   
}

function unTransformGraphElementOnUnselect( obj, event ){
    if ( obj.isGraphElement ) { obj.referent.unTransformOnDblClick(); } 
}

The logic of mouseEventHandler() still has some issues, but the core frustration is handled. Some additional refactoring worth mentioning:

  • It wasn't necessary to add the event.type to mouse.
  • I moved the mouseEventHandler() call from render() to onMouse(). That ensures that mouse events are registered only once.
  • I got rid of the 'fn'/'revFn' callbacks in mouseEventHandler() as they just made things more confusing than they need to be.
  • Event doesn't need to be a parameter for the transform functions, that'll be coming out shortly.

Hope this helps someone.