diff --git a/extlib/STLExporter.js b/extlib/STLExporter.js
deleted file mode 100644
index 606b17c..0000000
--- a/extlib/STLExporter.js
+++ /dev/null
@@ -1,209 +0,0 @@
-import {
- Vector3
-} from '../../../build/three.module.js';
-
-/**
- * Usage:
- * var exporter = new STLExporter();
- *
- * // second argument is a list of options
- * var data = exporter.parse( mesh, { binary: true } );
- *
- */
-
-var STLExporter = function () {};
-
-STLExporter.prototype = {
-
- constructor: STLExporter,
-
- parse: function ( scene, options ) {
-
- if ( options === undefined ) options = {};
-
- var binary = options.binary !== undefined ? options.binary : false;
-
- //
-
- var objects = [];
- var triangles = 0;
-
- scene.traverse( function ( object ) {
-
- if ( object.isMesh ) {
-
- var geometry = object.geometry;
-
- if ( geometry.isBufferGeometry !== true ) {
-
- throw new Error( 'THREE.STLExporter: Geometry is not of type THREE.BufferGeometry.' );
-
- }
-
- var index = geometry.index;
- var positionAttribute = geometry.getAttribute( 'position' );
-
- triangles += ( index !== null ) ? ( index.count / 3 ) : ( positionAttribute.count / 3 );
-
- objects.push( {
- object3d: object,
- geometry: geometry
- } );
-
- }
-
- } );
-
- var output;
- var offset = 80; // skip header
-
- if ( binary === true ) {
-
- var bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
- var arrayBuffer = new ArrayBuffer( bufferLength );
- output = new DataView( arrayBuffer );
- output.setUint32( offset, triangles, true ); offset += 4;
-
- } else {
-
- output = '';
- output += 'solid exported\n';
-
- }
-
- var vA = new Vector3();
- var vB = new Vector3();
- var vC = new Vector3();
- var cb = new Vector3();
- var ab = new Vector3();
- var normal = new Vector3();
-
- for ( var i = 0, il = objects.length; i < il; i ++ ) {
-
- var object = objects[ i ].object3d;
- var geometry = objects[ i ].geometry;
-
- var index = geometry.index;
- var positionAttribute = geometry.getAttribute( 'position' );
-
- if ( index !== null ) {
-
- // indexed geometry
-
- for ( var j = 0; j < index.count; j += 3 ) {
-
- var a = index.getX( j + 0 );
- var b = index.getX( j + 1 );
- var c = index.getX( j + 2 );
-
- writeFace( a, b, c, positionAttribute, object );
-
- }
-
- } else {
-
- // non-indexed geometry
-
- for ( var j = 0; j < positionAttribute.count; j += 3 ) {
-
- var a = j + 0;
- var b = j + 1;
- var c = j + 2;
-
- writeFace( a, b, c, positionAttribute, object );
-
- }
-
- }
-
- }
-
- if ( binary === false ) {
-
- output += 'endsolid exported\n';
-
- }
-
- return output;
-
- function writeFace( a, b, c, positionAttribute, object ) {
-
- vA.fromBufferAttribute( positionAttribute, a );
- vB.fromBufferAttribute( positionAttribute, b );
- vC.fromBufferAttribute( positionAttribute, c );
-
- if ( object.isSkinnedMesh === true ) {
-
- object.boneTransform( a, vA );
- object.boneTransform( b, vB );
- object.boneTransform( c, vC );
-
- }
-
- vA.applyMatrix4( object.matrixWorld );
- vB.applyMatrix4( object.matrixWorld );
- vC.applyMatrix4( object.matrixWorld );
-
- writeNormal( vA, vB, vC );
-
- writeVertex( vA );
- writeVertex( vB );
- writeVertex( vC );
-
- if ( binary === true ) {
-
- output.setUint16( offset, 0, true ); offset += 2;
-
- } else {
-
- output += '\t\tendloop\n';
- output += '\tendfacet\n';
-
- }
-
- }
-
- function writeNormal( vA, vB, vC ) {
-
- cb.subVectors( vC, vB );
- ab.subVectors( vA, vB );
- cb.cross( ab ).normalize();
-
- normal.copy( cb ).normalize();
-
- if ( binary === true ) {
-
- output.setFloat32( offset, normal.x, true ); offset += 4;
- output.setFloat32( offset, normal.y, true ); offset += 4;
- output.setFloat32( offset, normal.z, true ); offset += 4;
-
- } else {
-
- output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
- output += '\t\touter loop\n';
-
- }
-
- }
-
- function writeVertex( vertex ) {
-
- if ( binary === true ) {
-
- output.setFloat32( offset, vertex.x, true ); offset += 4;
- output.setFloat32( offset, vertex.y, true ); offset += 4;
- output.setFloat32( offset, vertex.z, true ); offset += 4;
-
- } else {
-
- output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
-
- }
-
- }
-
- }
-
-};
-
-export { STLExporter };
diff --git a/extlib/TrackballControls.js b/extlib/TrackballControls.js
deleted file mode 100644
index d110778..0000000
--- a/extlib/TrackballControls.js
+++ /dev/null
@@ -1,754 +0,0 @@
-import {
- EventDispatcher,
- MOUSE,
- Quaternion,
- Vector2,
- Vector3
-} from '../../../build/three.module.js';
-
-var TrackballControls = function ( object, domElement ) {
-
- if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
- if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
-
- var scope = this;
- var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
-
- this.object = object;
- this.domElement = domElement;
-
- // API
-
- this.enabled = true;
-
- this.screen = { left: 0, top: 0, width: 0, height: 0 };
-
- this.rotateSpeed = 1.0;
- this.zoomSpeed = 1.2;
- this.panSpeed = 0.3;
-
- this.noRotate = false;
- this.noZoom = false;
- this.noPan = false;
-
- this.staticMoving = false;
- this.dynamicDampingFactor = 0.2;
-
- this.minDistance = 0;
- this.maxDistance = Infinity;
-
- this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
-
- this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
-
- // internals
-
- this.target = new Vector3();
-
- var EPS = 0.000001;
-
- var lastPosition = new Vector3();
- var lastZoom = 1;
-
- var _state = STATE.NONE,
- _keyState = STATE.NONE,
-
- _eye = new Vector3(),
-
- _movePrev = new Vector2(),
- _moveCurr = new Vector2(),
-
- _lastAxis = new Vector3(),
- _lastAngle = 0,
-
- _zoomStart = new Vector2(),
- _zoomEnd = new Vector2(),
-
- _touchZoomDistanceStart = 0,
- _touchZoomDistanceEnd = 0,
-
- _panStart = new Vector2(),
- _panEnd = new Vector2();
-
- // for reset
-
- this.target0 = this.target.clone();
- this.position0 = this.object.position.clone();
- this.up0 = this.object.up.clone();
- this.zoom0 = this.object.zoom;
-
- // events
-
- var changeEvent = { type: 'change' };
- var startEvent = { type: 'start' };
- var endEvent = { type: 'end' };
-
-
- // methods
-
- this.handleResize = function () {
-
- var box = scope.domElement.getBoundingClientRect();
- // adjustments come from similar code in the jquery offset() function
- var d = scope.domElement.ownerDocument.documentElement;
- scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
- scope.screen.top = box.top + window.pageYOffset - d.clientTop;
- scope.screen.width = box.width;
- scope.screen.height = box.height;
-
- };
-
- var getMouseOnScreen = ( function () {
-
- var vector = new Vector2();
-
- return function getMouseOnScreen( pageX, pageY ) {
-
- vector.set(
- ( pageX - scope.screen.left ) / scope.screen.width,
- ( pageY - scope.screen.top ) / scope.screen.height
- );
-
- return vector;
-
- };
-
- }() );
-
- var getMouseOnCircle = ( function () {
-
- var vector = new Vector2();
-
- return function getMouseOnCircle( pageX, pageY ) {
-
- vector.set(
- ( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
- ( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
- );
-
- return vector;
-
- };
-
- }() );
-
- this.rotateCamera = ( function () {
-
- var axis = new Vector3(),
- quaternion = new Quaternion(),
- eyeDirection = new Vector3(),
- objectUpDirection = new Vector3(),
- objectSidewaysDirection = new Vector3(),
- moveDirection = new Vector3(),
- angle;
-
- return function rotateCamera() {
-
- moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
- angle = moveDirection.length();
-
- if ( angle ) {
-
- _eye.copy( scope.object.position ).sub( scope.target );
-
- eyeDirection.copy( _eye ).normalize();
- objectUpDirection.copy( scope.object.up ).normalize();
- objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
-
- objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
- objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
-
- moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
-
- axis.crossVectors( moveDirection, _eye ).normalize();
-
- angle *= scope.rotateSpeed;
- quaternion.setFromAxisAngle( axis, angle );
-
- _eye.applyQuaternion( quaternion );
- scope.object.up.applyQuaternion( quaternion );
-
- _lastAxis.copy( axis );
- _lastAngle = angle;
-
- } else if ( ! scope.staticMoving && _lastAngle ) {
-
- _lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
- _eye.copy( scope.object.position ).sub( scope.target );
- quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
- _eye.applyQuaternion( quaternion );
- scope.object.up.applyQuaternion( quaternion );
-
- }
-
- _movePrev.copy( _moveCurr );
-
- };
-
- }() );
-
-
- this.zoomCamera = function () {
-
- var factor;
-
- if ( _state === STATE.TOUCH_ZOOM_PAN ) {
-
- factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
- _touchZoomDistanceStart = _touchZoomDistanceEnd;
-
- if ( scope.object.isPerspectiveCamera ) {
-
- _eye.multiplyScalar( factor );
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.zoom *= factor;
- scope.object.updateProjectionMatrix();
-
- } else {
-
- console.warn( 'THREE.TrackballControls: Unsupported camera type' );
-
- }
-
- } else {
-
- factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
-
- if ( factor !== 1.0 && factor > 0.0 ) {
-
- if ( scope.object.isPerspectiveCamera ) {
-
- _eye.multiplyScalar( factor );
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.zoom /= factor;
- scope.object.updateProjectionMatrix();
-
- } else {
-
- console.warn( 'THREE.TrackballControls: Unsupported camera type' );
-
- }
-
- }
-
- if ( scope.staticMoving ) {
-
- _zoomStart.copy( _zoomEnd );
-
- } else {
-
- _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
-
- }
-
- }
-
- };
-
- this.panCamera = ( function () {
-
- var mouseChange = new Vector2(),
- objectUp = new Vector3(),
- pan = new Vector3();
-
- return function panCamera() {
-
- mouseChange.copy( _panEnd ).sub( _panStart );
-
- if ( mouseChange.lengthSq() ) {
-
- if ( scope.object.isOrthographicCamera ) {
-
- var scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
- var scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
-
- mouseChange.x *= scale_x;
- mouseChange.y *= scale_y;
-
- }
-
- mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
-
- pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
- pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
-
- scope.object.position.add( pan );
- scope.target.add( pan );
-
- if ( scope.staticMoving ) {
-
- _panStart.copy( _panEnd );
-
- } else {
-
- _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
-
- }
-
- }
-
- };
-
- }() );
-
- this.checkDistances = function () {
-
- if ( ! scope.noZoom || ! scope.noPan ) {
-
- if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
-
- scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
- _zoomStart.copy( _zoomEnd );
-
- }
-
- if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
-
- scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
- _zoomStart.copy( _zoomEnd );
-
- }
-
- }
-
- };
-
- this.update = function () {
-
- _eye.subVectors( scope.object.position, scope.target );
-
- if ( ! scope.noRotate ) {
-
- scope.rotateCamera();
-
- }
-
- if ( ! scope.noZoom ) {
-
- scope.zoomCamera();
-
- }
-
- if ( ! scope.noPan ) {
-
- scope.panCamera();
-
- }
-
- scope.object.position.addVectors( scope.target, _eye );
-
- if ( scope.object.isPerspectiveCamera ) {
-
- scope.checkDistances();
-
- scope.object.lookAt( scope.target );
-
- if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
-
- scope.dispatchEvent( changeEvent );
-
- lastPosition.copy( scope.object.position );
-
- }
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.lookAt( scope.target );
-
- if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
-
- scope.dispatchEvent( changeEvent );
-
- lastPosition.copy( scope.object.position );
- lastZoom = scope.object.zoom;
-
- }
-
- } else {
-
- console.warn( 'THREE.TrackballControls: Unsupported camera type' );
-
- }
-
- };
-
- this.reset = function () {
-
- _state = STATE.NONE;
- _keyState = STATE.NONE;
-
- scope.target.copy( scope.target0 );
- scope.object.position.copy( scope.position0 );
- scope.object.up.copy( scope.up0 );
- scope.object.zoom = scope.zoom0;
-
- scope.object.updateProjectionMatrix();
-
- _eye.subVectors( scope.object.position, scope.target );
-
- scope.object.lookAt( scope.target );
-
- scope.dispatchEvent( changeEvent );
-
- lastPosition.copy( scope.object.position );
- lastZoom = scope.object.zoom;
-
- };
-
- // listeners
-
- function onPointerDown( event ) {
-
- if ( scope.enabled === false ) return;
-
- switch ( event.pointerType ) {
-
- case 'mouse':
- case 'pen':
- onMouseDown( event );
- break;
-
- // TODO touch
-
- }
-
- }
-
- function onPointerMove( event ) {
-
- if ( scope.enabled === false ) return;
-
- switch ( event.pointerType ) {
-
- case 'mouse':
- case 'pen':
- onMouseMove( event );
- break;
-
- // TODO touch
-
- }
-
- }
-
- function onPointerUp( event ) {
-
- if ( scope.enabled === false ) return;
-
- switch ( event.pointerType ) {
-
- case 'mouse':
- case 'pen':
- onMouseUp( event );
- break;
-
- // TODO touch
-
- }
-
- }
-
- function keydown( event ) {
-
- if ( scope.enabled === false ) return;
-
- window.removeEventListener( 'keydown', keydown );
-
- if ( _keyState !== STATE.NONE ) {
-
- return;
-
- } else if ( event.keyCode === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
-
- _keyState = STATE.ROTATE;
-
- } else if ( event.keyCode === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
-
- _keyState = STATE.ZOOM;
-
- } else if ( event.keyCode === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
-
- _keyState = STATE.PAN;
-
- }
-
- }
-
- function keyup() {
-
- if ( scope.enabled === false ) return;
-
- _keyState = STATE.NONE;
-
- window.addEventListener( 'keydown', keydown );
-
- }
-
- function onMouseDown( event ) {
-
- event.preventDefault();
- event.stopPropagation();
-
- if ( _state === STATE.NONE ) {
-
- switch ( event.button ) {
-
- case scope.mouseButtons.LEFT:
- _state = STATE.ROTATE;
- break;
-
- case scope.mouseButtons.MIDDLE:
- _state = STATE.ZOOM;
- break;
-
- case scope.mouseButtons.RIGHT:
- _state = STATE.PAN;
- break;
-
- default:
- _state = STATE.NONE;
-
- }
-
- }
-
- var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
-
- if ( state === STATE.ROTATE && ! scope.noRotate ) {
-
- _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
- _movePrev.copy( _moveCurr );
-
- } else if ( state === STATE.ZOOM && ! scope.noZoom ) {
-
- _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
- _zoomEnd.copy( _zoomStart );
-
- } else if ( state === STATE.PAN && ! scope.noPan ) {
-
- _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
- _panEnd.copy( _panStart );
-
- }
-
- scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
- scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
-
- scope.dispatchEvent( startEvent );
-
- }
-
- function onMouseMove( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
-
- if ( state === STATE.ROTATE && ! scope.noRotate ) {
-
- _movePrev.copy( _moveCurr );
- _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
-
- } else if ( state === STATE.ZOOM && ! scope.noZoom ) {
-
- _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-
- } else if ( state === STATE.PAN && ! scope.noPan ) {
-
- _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-
- }
-
- }
-
- function onMouseUp( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- _state = STATE.NONE;
-
- scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
- scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
-
- scope.dispatchEvent( endEvent );
-
- }
-
- function mousewheel( event ) {
-
- if ( scope.enabled === false ) return;
-
- if ( scope.noZoom === true ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- switch ( event.deltaMode ) {
-
- case 2:
- // Zoom in pages
- _zoomStart.y -= event.deltaY * 0.025;
- break;
-
- case 1:
- // Zoom in lines
- _zoomStart.y -= event.deltaY * 0.01;
- break;
-
- default:
- // undefined, 0, assume pixels
- _zoomStart.y -= event.deltaY * 0.00025;
- break;
-
- }
-
- scope.dispatchEvent( startEvent );
- scope.dispatchEvent( endEvent );
-
- }
-
- function touchstart( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- switch ( event.touches.length ) {
-
- case 1:
- _state = STATE.TOUCH_ROTATE;
- _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
- _movePrev.copy( _moveCurr );
- break;
-
- default: // 2 or more
- _state = STATE.TOUCH_ZOOM_PAN;
- var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
- var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
- _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
-
- var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
- var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
- _panStart.copy( getMouseOnScreen( x, y ) );
- _panEnd.copy( _panStart );
- break;
-
- }
-
- scope.dispatchEvent( startEvent );
-
- }
-
- function touchmove( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- switch ( event.touches.length ) {
-
- case 1:
- _movePrev.copy( _moveCurr );
- _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
- break;
-
- default: // 2 or more
- var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
- var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
- _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
-
- var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
- var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
- _panEnd.copy( getMouseOnScreen( x, y ) );
- break;
-
- }
-
- }
-
- function touchend( event ) {
-
- if ( scope.enabled === false ) return;
-
- switch ( event.touches.length ) {
-
- case 0:
- _state = STATE.NONE;
- break;
-
- case 1:
- _state = STATE.TOUCH_ROTATE;
- _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
- _movePrev.copy( _moveCurr );
- break;
-
- }
-
- scope.dispatchEvent( endEvent );
-
- }
-
- function contextmenu( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- }
-
- this.dispose = function () {
-
- scope.domElement.removeEventListener( 'contextmenu', contextmenu );
-
- scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
- scope.domElement.removeEventListener( 'wheel', mousewheel );
-
- scope.domElement.removeEventListener( 'touchstart', touchstart );
- scope.domElement.removeEventListener( 'touchend', touchend );
- scope.domElement.removeEventListener( 'touchmove', touchmove );
-
- scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
- scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
-
- window.removeEventListener( 'keydown', keydown );
- window.removeEventListener( 'keyup', keyup );
-
- };
-
- this.domElement.addEventListener( 'contextmenu', contextmenu );
-
- this.domElement.addEventListener( 'pointerdown', onPointerDown );
- this.domElement.addEventListener( 'wheel', mousewheel );
-
- this.domElement.addEventListener( 'touchstart', touchstart );
- this.domElement.addEventListener( 'touchend', touchend );
- this.domElement.addEventListener( 'touchmove', touchmove );
-
- this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
- this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
-
- window.addEventListener( 'keydown', keydown );
- window.addEventListener( 'keyup', keyup );
-
- this.handleResize();
-
- // force an update at start
- this.update();
-
-};
-
-TrackballControls.prototype = Object.create( EventDispatcher.prototype );
-TrackballControls.prototype.constructor = TrackballControls;
-
-export { TrackballControls };
diff --git a/extlib/fs/directory-open.mjs b/extlib/fs/directory-open.mjs
deleted file mode 100644
index e51d720..0000000
--- a/extlib/fs/directory-open.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-import supported from './supported.mjs';
-
-const implementation = !supported
- ? import('./legacy/directory-open.mjs')
- : supported === 'chooseFileSystemEntries'
- ? import('./fs-access-legacy/directory-open.mjs')
- : import('./fs-access/directory-open.mjs');
-
-/**
- * For opening directories, dynamically either loads the File System Access API
- * module or the legacy method.
- */
-export async function directoryOpen(...args) {
- return (await implementation).default(...args);
-}
diff --git a/extlib/fs/file-open.mjs b/extlib/fs/file-open.mjs
deleted file mode 100644
index 407082c..0000000
--- a/extlib/fs/file-open.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-import supported from './supported.mjs';
-
-const implementation = !supported
- ? import('./legacy/file-open.mjs')
- : supported === 'chooseFileSystemEntries'
- ? import('./fs-access-legacy/file-open.mjs')
- : import('./fs-access/file-open.mjs');
-
-/**
- * For opening files, dynamically either loads the File System Access API module
- * or the legacy method.
- */
-export async function fileOpen(...args) {
- return (await implementation).default(...args);
-}
diff --git a/extlib/fs/file-save.mjs b/extlib/fs/file-save.mjs
deleted file mode 100644
index 8364a05..0000000
--- a/extlib/fs/file-save.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-import supported from './supported.mjs';
-
-const implementation = !supported
- ? import('./legacy/file-save.mjs')
- : supported === 'chooseFileSystemEntries'
- ? import('./fs-access-legacy/file-save.mjs')
- : import('./fs-access/file-save.mjs');
-
-/**
- * For saving files, dynamically either loads the File System Access API module
- * or the legacy method.
- */
-export async function fileSave(...args) {
- return (await implementation).default(...args);
-}
diff --git a/extlib/fs/fs-access-legacy/directory-open.mjs b/extlib/fs/fs-access-legacy/directory-open.mjs
deleted file mode 100644
index e921f95..0000000
--- a/extlib/fs/fs-access-legacy/directory-open.mjs
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-const getFiles = async (dirHandle, recursive, path = dirHandle.name) => {
- const dirs = [];
- const files = [];
- for await (const entry of dirHandle.getEntries()) {
- const nestedPath = `${path}/${entry.name}`;
- if (entry.isFile) {
- files.push(
- entry.getFile().then((file) =>
- Object.defineProperty(file, 'webkitRelativePath', {
- configurable: true,
- enumerable: true,
- get: () => nestedPath,
- })
- )
- );
- } else if (entry.isDirectory && recursive) {
- dirs.push(getFiles(entry, recursive, nestedPath));
- }
- }
- return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))];
-};
-
-/**
- * Opens a directory from disk using the (legacy) File System Access API.
- * @type { typeof import("../../index").directoryOpen }
- */
-export default async (options = {}) => {
- options.recursive = options.recursive || false;
- const handle = await window.chooseFileSystemEntries({
- type: 'open-directory',
- });
- return getFiles(handle, options.recursive);
-};
diff --git a/extlib/fs/fs-access-legacy/file-open.mjs b/extlib/fs/fs-access-legacy/file-open.mjs
deleted file mode 100644
index 2cdfe6b..0000000
--- a/extlib/fs/fs-access-legacy/file-open.mjs
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-const getFileWithHandle = async (handle) => {
- const file = await handle.getFile();
- file.handle = handle;
- return file;
-};
-
-/**
- * Opens a file from disk using the (legacy) File System Access API.
- * @type { typeof import("../../index").fileOpen }
- */
-export default async (options = {}) => {
- const handleOrHandles = await window.chooseFileSystemEntries({
- accepts: [
- {
- description: options.description || '',
- mimeTypes: options.mimeTypes || ['*/*'],
- extensions: options.extensions || [''],
- },
- ],
- multiple: options.multiple || false,
- });
- if (options.multiple) {
- return Promise.all(handleOrHandles.map(getFileWithHandle));
- }
- return getFileWithHandle(handleOrHandles);
-};
diff --git a/extlib/fs/fs-access-legacy/file-save.mjs b/extlib/fs/fs-access-legacy/file-save.mjs
deleted file mode 100644
index 9fe0ca6..0000000
--- a/extlib/fs/fs-access-legacy/file-save.mjs
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * Saves a file to disk using the (legacy) File System Access API.
- * @type { typeof import("../../index").fileSave }
- */
-export default async (blob, options = {}, handle = null) => {
- options.fileName = options.fileName || 'Untitled';
- handle =
- handle ||
- (await window.chooseFileSystemEntries({
- type: 'save-file',
- accepts: [
- {
- description: options.description || '',
- mimeTypes: [blob.type],
- extensions: options.extensions || [''],
- },
- ],
- }));
- const writable = await handle.createWritable();
- await writable.write(blob);
- await writable.close();
- return handle;
-};
diff --git a/extlib/fs/fs-access/directory-open.mjs b/extlib/fs/fs-access/directory-open.mjs
deleted file mode 100644
index 19fade2..0000000
--- a/extlib/fs/fs-access/directory-open.mjs
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-const getFiles = async (dirHandle, recursive, path = dirHandle.name) => {
- const dirs = [];
- const files = [];
- for await (const entry of dirHandle.values()) {
- const nestedPath = `${path}/${entry.name}`;
- if (entry.kind === 'file') {
- files.push(
- entry.getFile().then((file) =>
- Object.defineProperty(file, 'webkitRelativePath', {
- configurable: true,
- enumerable: true,
- get: () => nestedPath,
- })
- )
- );
- } else if (entry.kind === 'directory' && recursive) {
- dirs.push(getFiles(entry, recursive, nestedPath));
- }
- }
- return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))];
-};
-
-/**
- * Opens a directory from disk using the File System Access API.
- * @type { typeof import("../../index").directoryOpen }
- */
-export default async (options = {}) => {
- options.recursive = options.recursive || false;
- const handle = await window.showDirectoryPicker();
- return getFiles(handle, options.recursive);
-};
diff --git a/extlib/fs/fs-access/file-open.mjs b/extlib/fs/fs-access/file-open.mjs
deleted file mode 100644
index dab1119..0000000
--- a/extlib/fs/fs-access/file-open.mjs
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-const getFileWithHandle = async (handle) => {
- const file = await handle.getFile();
- file.handle = handle;
- return file;
-};
-
-/**
- * Opens a file from disk using the File System Access API.
- * @type { typeof import("../../index").fileOpen }
- */
-export default async (options = {}) => {
- const accept = {};
- if (options.mimeTypes) {
- options.mimeTypes.map((mimeType) => {
- accept[mimeType] = options.extensions || [];
- });
- } else {
- accept['*/*'] = options.extensions || [];
- }
- const handleOrHandles = await window.showOpenFilePicker({
- types: [
- {
- description: options.description || '',
- accept: accept,
- },
- ],
- multiple: options.multiple || false,
- });
- const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
- if (options.multiple) {
- return files;
- }
- return files[0];
-};
diff --git a/extlib/fs/fs-access/file-save.mjs b/extlib/fs/fs-access/file-save.mjs
deleted file mode 100644
index 16b8b2c..0000000
--- a/extlib/fs/fs-access/file-save.mjs
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * Saves a file to disk using the File System Access API.
- * @type { typeof import("../../index").fileSave }
- */
-export default async (
- blob,
- options = {},
- existingHandle = null,
- throwIfExistingHandleNotGood = false
-) => {
- options.fileName = options.fileName || 'Untitled';
- const accept = {};
- if (options.mimeTypes) {
- options.mimeTypes.push(blob.type);
- options.mimeTypes.map((mimeType) => {
- accept[mimeType] = options.extensions || [];
- });
- } else {
- accept[blob.type] = options.extensions || [];
- }
- if (existingHandle) {
- try {
- // Check if the file still exists.
- await existingHandle.getFile();
- } catch (err) {
- existingHandle = null;
- if (throwIfExistingHandleNotGood) {
- throw err;
- }
- }
- }
- const handle =
- existingHandle ||
- (await window.showSaveFilePicker({
- suggestedName: options.fileName,
- types: [
- {
- description: options.description || '',
- accept: accept,
- },
- ],
- }));
- const writable = await handle.createWritable();
- await writable.write(blob);
- await writable.close();
- return handle;
-};
diff --git a/extlib/fs/index.js b/extlib/fs/index.js
deleted file mode 100644
index f67f6df..0000000
--- a/extlib/fs/index.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * @module browser-fs-access
- */
-export { fileOpen } from './file-open.mjs';
-export { directoryOpen } from './directory-open.mjs';
-export { fileSave } from './file-save.mjs';
-export { default as supported } from './supported.mjs';
diff --git a/extlib/fs/legacy/directory-open.mjs b/extlib/fs/legacy/directory-open.mjs
deleted file mode 100644
index e58a870..0000000
--- a/extlib/fs/legacy/directory-open.mjs
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * Opens a directory from disk using the legacy
- * `` method.
- * @type { typeof import("../../index").directoryOpen }
- */
-export default async (options = {}) => {
- options.recursive = options.recursive || false;
- return new Promise((resolve, reject) => {
- const input = document.createElement('input');
- input.type = 'file';
- input.webkitdirectory = true;
-
- // ToDo: Remove this workaround once
- // https://github.com/whatwg/html/issues/6376 is specified and supported.
- const rejectOnPageInteraction = () => {
- window.removeEventListener('pointermove', rejectOnPageInteraction);
- window.removeEventListener('pointerdown', rejectOnPageInteraction);
- window.removeEventListener('keydown', rejectOnPageInteraction);
- reject(new DOMException('The user aborted a request.', 'AbortError'));
- };
-
- window.addEventListener('pointermove', rejectOnPageInteraction);
- window.addEventListener('pointerdown', rejectOnPageInteraction);
- window.addEventListener('keydown', rejectOnPageInteraction);
-
- input.addEventListener('change', () => {
- window.removeEventListener('pointermove', rejectOnPageInteraction);
- window.removeEventListener('pointerdown', rejectOnPageInteraction);
- window.removeEventListener('keydown', rejectOnPageInteraction);
- let files = Array.from(input.files);
- if (!options.recursive) {
- files = files.filter((file) => {
- return file.webkitRelativePath.split('/').length === 2;
- });
- }
- resolve(files);
- });
-
- input.click();
- });
-};
diff --git a/extlib/fs/legacy/file-open.mjs b/extlib/fs/legacy/file-open.mjs
deleted file mode 100644
index cfa4f7f..0000000
--- a/extlib/fs/legacy/file-open.mjs
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * Opens a file from disk using the legacy `` method.
- * @type { typeof import("../../index").fileOpen }
- */
-export default async (options = {}) => {
- return new Promise((resolve, reject) => {
- const input = document.createElement('input');
- input.type = 'file';
- const accept = [
- ...(options.mimeTypes ? options.mimeTypes : []),
- options.extensions ? options.extensions : [],
- ].join();
- input.multiple = options.multiple || false;
- // Empty string allows everything.
- input.accept = accept || '';
-
- // ToDo: Remove this workaround once
- // https://github.com/whatwg/html/issues/6376 is specified and supported.
- const rejectOnPageInteraction = () => {
- window.removeEventListener('pointermove', rejectOnPageInteraction);
- window.removeEventListener('pointerdown', rejectOnPageInteraction);
- window.removeEventListener('keydown', rejectOnPageInteraction);
- reject(new DOMException('The user aborted a request.', 'AbortError'));
- };
-
- window.addEventListener('pointermove', rejectOnPageInteraction);
- window.addEventListener('pointerdown', rejectOnPageInteraction);
- window.addEventListener('keydown', rejectOnPageInteraction);
-
- input.addEventListener('change', () => {
- window.removeEventListener('pointermove', rejectOnPageInteraction);
- window.removeEventListener('pointerdown', rejectOnPageInteraction);
- window.removeEventListener('keydown', rejectOnPageInteraction);
- resolve(input.multiple ? input.files : input.files[0]);
- });
-
- input.click();
- });
-};
diff --git a/extlib/fs/legacy/file-save.mjs b/extlib/fs/legacy/file-save.mjs
deleted file mode 100644
index da453f7..0000000
--- a/extlib/fs/legacy/file-save.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * Saves a file to disk using the legacy `` method.
- * @type { typeof import("../../index").fileSave }
- */
-export default async (blob, options = {}) => {
- const a = document.createElement('a');
- a.download = options.fileName || 'Untitled';
- a.href = URL.createObjectURL(blob);
- a.addEventListener('click', () => {
- // `setTimeout()` due to
- // https://github.com/LLK/scratch-gui/issues/1783#issuecomment-426286393
- setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
- });
- a.click();
-};
diff --git a/extlib/fs/supported.mjs b/extlib/fs/supported.mjs
deleted file mode 100644
index b1ac42a..0000000
--- a/extlib/fs/supported.mjs
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
-
-/**
- * Returns whether the File System Access API is supported and usable in the
- * current context (for example cross-origin iframes).
- * @returns {boolean} Returns `true` if the File System Access API is supported and usable, else returns `false`.
- */
-const supported = (() => {
- // ToDo: Remove this check once Permissions Policy integration
- // has happened, tracked in
- // https://github.com/WICG/file-system-access/issues/245.
- if ('top' in self && self !== top) {
- try {
- // This will succeed on same-origin iframes,
- // but fail on cross-origin iframes.
- top.location + '';
- } catch {
- return false;
- }
- } else if ('chooseFileSystemEntries' in self) {
- return 'chooseFileSystemEntries';
- } else if ('showOpenFilePicker' in self) {
- return 'showOpenFilePicker';
- }
- return false;
-})();
-
-export default supported;
diff --git a/extlib/trackball.js b/extlib/trackball.js
index ae263f9..f7e000f 100644
--- a/extlib/trackball.js
+++ b/extlib/trackball.js
@@ -194,7 +194,8 @@ var TrackballControls = function ( object, domElement ) {
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
- factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+ // factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+ factor = _touchZoomDistanceEnd / _touchZoomDistanceStart;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
if ( scope.object.isPerspectiveCamera ) {