anchor.diff:  
       _zoomStart = new Vector2.zero();
       _zoomEnd = new Vector2.zero();
  +
  +    _touchZoomDistanceStart = 0.0;
  +    _touchZoomDistanceEnd = 0.0;
   
       _panStart = new Vector2.zero();
       _panEnd = new Vector2.zero();
  
positive.code:  static const ROTATE =  0;
   static const ZOOM = 1;
   static const PAN = 2;
  }
  
  class TrackballControls extends EventEmitter {
  
negative.code:           _rotateEnd = getMouseProjectionOnBall( event.touches[ 0 ].page.x, event.touches[ 0 ].page.y );
            break;
          case 2:
            var dx = event.touches[ 0 ].page.x - event.touches[ 1 ].page.x;
            var dy = event.touches[ 0 ].page.y - event.touches[ 1 ].page.y;
            _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
  
            var x = ( event.touches[ 0 ].page.x + event.touches[ 1 ].page.x ) / 2;
            var y = ( event.touches[ 0 ].page.y + event.touches[ 1 ].page.y ) / 2;
            _panEnd = getMouseOnScreen( x, y );
            break;
          default:
            _state = STATE.NONE;
            break;
  
        }
  
      }
  
      touchend( TouchEvent event ) {
  
        if ( !enabled ) { return; }
  
        switch ( event.touches.length ) {
  
          case 1:
            _rotateEnd = getMouseProjectionOnBall( event.touches[ 0 ].page.x, event.touches[ 0 ].page.y );
            _rotateStart.setFrom( _rotateEnd );
            break;
  
          case 2:
            _touchZoomDistanceStart = _touchZoomDistanceEnd = 0.0;
  
            var x = ( event.touches[ 0 ].page.x + event.touches[ 1 ].page.x ) / 2;
            var y = ( event.touches[ 0 ].page.y + event.touches[ 1 ].page.y ) / 2;
            _panEnd =  getMouseOnScreen( x, y );
            _panStart.setFrom( _panEnd );
            break;
  
        }
  
        _state = STATE.NONE;
      }
  
      void unlisten() {
  