Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port of Orbit.js #19

Open
bartolomiew opened this issue Feb 18, 2015 · 2 comments
Open

port of Orbit.js #19

bartolomiew opened this issue Feb 18, 2015 · 2 comments

Comments

@bartolomiew
Copy link

TrackballControls works fine but I think Orbit is better so I've made a simple port of Orbit.js, you are free to use it

// bartolomiew
package thothbot.parallax.core.client.controls;

import thothbot.parallax.core.shared.core.Object3D;
import thothbot.parallax.core.shared.math.Vector2;
import thothbot.parallax.core.shared.math.Vector3;

import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class OrbitControls extends Controls implements MouseMoveHandler, MouseDownHandler, MouseUpHandler, 
KeyDownHandler, KeyUpHandler, ContextMenuHandler, MouseWheelHandler {

    private enum STATE {
         NONE,
         ROTATE, 
         ZOOM, 
         PAN
    };

    private boolean enabled = true;

    private Vector3 center = new Vector3();

    private boolean isZoom = true;
    private double userZoomSpeed = 1.0;

    private boolean isRotate = true;
    private double userRotateSpeed = 1.0;

    private boolean isPan = true;
    private double userPanSpeed = 2.0;

    private boolean autoRotate = false;
    private double autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60

    private double minPolarAngle = 0; // radians
    private double maxPolarAngle = Math.PI; // radians

    private double minDistance = 0;
    private double maxDistance = Double.MAX_VALUE;

    // 65 /*A*/, 83 /*S*/, 68 /*D*/
    //  this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 };
    private int keyRotate = 65; // A
    private int keyZoom = 83; // S
    private int keyPan = 68; // D

    private Vector2 rotateStart;
    private Vector2 rotateEnd;
    private Vector2 rotateDelta;

    private Vector2 zoomStart;
    private Vector2 zoomEnd;
    private Vector2 zoomDelta;

    private Vector3 lastPosition;

    private double phiDelta;
    private double thetaDelta;
    private double scale;

    private boolean isKeyPressed = false;
    private STATE state = STATE.NONE;

    private static final double EPS = 0.000001;
    private static final double PIXELS_PER_ROUND = 1800;

    public OrbitControls(Object3D object, Widget widget) 
    {
        super(object, widget);

        if(getWidget().getClass() != RootPanel.class)
            getWidget().getElement().setAttribute( "tabindex", "-1" );

        rotateStart = new Vector2();
        rotateEnd = new Vector2();
        rotateDelta = new Vector2();
        zoomStart = new Vector2();
        zoomEnd = new Vector2();
        zoomDelta = new Vector2();
        phiDelta = 0;
        thetaDelta = 0;
        scale = 1;
        lastPosition = new Vector3();

        getWidget().addDomHandler(this, ContextMenuEvent.getType());

        getWidget().addDomHandler(this, MouseMoveEvent.getType());
        getWidget().addDomHandler(this, MouseDownEvent.getType());
        getWidget().addDomHandler(this, MouseUpEvent.getType());
        getWidget().addDomHandler(this, MouseWheelEvent.getType());
        RootPanel.get().addDomHandler(this, KeyDownEvent.getType());
        RootPanel.get().addDomHandler(this, KeyUpEvent.getType());  
    }

    @Override
    public void onContextMenu(ContextMenuEvent event) {
        event.preventDefault();
    }

    @Override
    public void onKeyUp(KeyUpEvent event) {
        if ( !enabled ) return;

        state = STATE.NONE;
    }

    @Override
    public void onKeyDown(KeyDownEvent event) {
        if ( !enabled ) return;
        if ( !isPan ) return;
        if ( state != STATE.NONE ) 
        {
            return;
        } 
        else if ( event.getNativeEvent().getKeyCode() == this.keyRotate && this.isRotate ) 
        {
            state = STATE.ROTATE;
        } 
        else if ( event.getNativeEvent().getKeyCode() == this.keyZoom && this.isZoom ) 
        {
            state = STATE.ZOOM;
        } 
        else if ( event.getNativeEvent().getKeyCode() == this.keyPan && this.isPan ) 
        {
            state = STATE.PAN;
        }

        if ( state != STATE.NONE )
            this.isKeyPressed = true;

    }

    @Override
    public void onMouseUp(MouseUpEvent event) {
        if ( !enabled ) return;
        if ( !isRotate ) return;

        event.preventDefault();
        event.stopPropagation();

        this.state = STATE.NONE;
    }

    @Override
    public void onMouseDown(MouseDownEvent event) {
        if ( !enabled ) return;
        if ( !isRotate ) return;

        event.preventDefault();
        event.stopPropagation();

        if ( state == STATE.NONE ) 
        {
            if ( event.getNativeButton() == NativeEvent.BUTTON_LEFT && this.isRotate ) 
            {
                state = STATE.ROTATE;
            } 
            else if ( event.getNativeButton() == NativeEvent.BUTTON_MIDDLE && this.isZoom ) 
            {
                state = STATE.ZOOM;
            } 
            else if ( event.getNativeButton() == NativeEvent.BUTTON_RIGHT && this.isPan ) 
            {
                state = STATE.PAN;
            }
        }

        if ( state == STATE.ROTATE ) {
            rotateStart.set( event.getClientX(), event.getClientY() );
        } else if ( state == STATE.ZOOM ) {
            zoomStart.set( event.getClientX(), event.getClientY() );
        } else if ( state == STATE.PAN ) {
        }
    }

    private int previousX;
    private int previousY;

    @Override
    public void onMouseMove(MouseMoveEvent event) {
        if ( !enabled ) return;
        event.preventDefault();

        previousX = event.getClientX();
        previousY = event.getClientY();

        if ( state == STATE.ROTATE ) {
            rotateEnd.set( event.getClientX(), event.getClientY() );
            rotateDelta.sub( rotateEnd, rotateStart );
            rotateLeft( 2 * Math.PI * rotateDelta.getX() / PIXELS_PER_ROUND * userRotateSpeed );
            rotateUp( 2 * Math.PI * rotateDelta.getY() / PIXELS_PER_ROUND * userRotateSpeed );
            rotateStart.copy( rotateEnd );
        } else if ( state == STATE.ZOOM ) {
            zoomEnd.set( event.getClientX(), event.getClientY() );
            zoomDelta.sub( zoomEnd, zoomStart );
            if ( zoomDelta.getY() > 0 ) {
                zoomIn(getZoomScale());
            } else {
                zoomOut(getZoomScale());
            }
            zoomStart.copy( zoomEnd );
        } else if ( state == STATE.PAN ) {
            int movementX = previousX - event.getClientX();
            int movementY = previousY - event.getClientY();
            pan( new Vector3( - movementX, movementY, 0 ) );
        }
    }

    @Override
    public void onMouseWheel(MouseWheelEvent event) {
        if ( !enabled ) return;
        if ( !isZoom ) return;
        int delta = event.getDeltaY();
        if ( delta > 0 ) {
            zoomOut(getZoomScale());
        } else {
            zoomIn(getZoomScale());
        }
    }

    public void rotateLeft( double angle ) {
        thetaDelta -= angle;

    }
    public void rotateRight( double angle ) {
        thetaDelta += angle;
    }

    public void rotateUp( double angle ) {
        phiDelta -= angle;
    }

    public void rotateDown( double angle ) {
        phiDelta += angle;
    }

    public void zoomIn( double zoomScale ) {
        scale /= zoomScale;
    }

    public void zoomOut( double zoomScale ) {
        scale *= zoomScale;
    }

    public void pan( Vector3 distance ) {
        distance.transformDirection( getObject().getMatrix() );
        distance.multiply( userPanSpeed );
        getObject().getPosition().add( distance );
        center.add( distance );
    }

    public void update() {
        Vector3 position = getObject().getPosition();
        Vector3 offset = position.clone().sub( this.center );

        // angle from z-axis around y-axis
        double theta = Math.atan2( offset.getX(), offset.getZ() );

        // angle from y-axis
        double phi = Math.atan2( Math.sqrt( offset.getX() * offset.getX() + offset.getZ() * offset.getZ() ), offset.getY() );

        if ( this.autoRotate ) {
            rotateLeft( getAutoRotationAngle() );
        }

        theta += thetaDelta;
        phi += phiDelta;

        // restrict phi to be between desired limits
        phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );

        // restrict phi to be between EPS and PI-EPS
        phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );

        double radius = offset.length() * scale;

        // restrict radius to be between desired limits
        radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );

        offset.setX(radius * Math.sin( phi ) * Math.sin( theta ));
        offset.setY(radius * Math.cos( phi ));
        offset.setZ(radius * Math.sin( phi ) * Math.cos( theta ));

        position.copy( this.center ).add( offset );

        getObject().lookAt( this.center );

        thetaDelta = 0;
        phiDelta = 0;
        scale = 1;

        if ( lastPosition.distanceTo( getObject().getPosition() ) > 0 ) {
            lastPosition.copy( getObject().getPosition() );
        }

    }

    public double getAutoRotationAngle() {
        return 2 * Math.PI / 60 / 60 * autoRotateSpeed;
    }

    public double getZoomScale() {
        return Math.pow( 0.95, userZoomSpeed );
    }


}
@bartolomiew
Copy link
Author

Some setters are missing but it enough for the demo I'm working on

@thothbot
Copy link
Owner

Thank you.
You can make a pull request to include your code to Parallax. I think it will be in v1.6 or early if we have critical issues.

@thothbot thothbot added this to the ver 2.0 milestone Jan 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants