source: js/viewer-f0/js/library/TrackballControls.js @ 193

Last change on this file since 193 was 193, checked in by Maciej Komosinski, 10 years ago

Set svn:eol-style native for all textual files

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 12.2 KB
Line 
1/**
2 * @author Eberhard Graether / http://egraether.com/
3 * @author Mark Lundin  / http://mark-lundin.com
4 */
5
6THREE.TrackballControls = function ( object, domElement ) {
7
8        var _this = this;
9        var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
10
11        this.object = object;
12        this.domElement = ( domElement !== undefined ) ? domElement : document;
13
14        // API
15
16        this.enabled = true;
17
18        this.screen = { left: 0, top: 0, width: 0, height: 0 };
19
20        this.rotateSpeed = 1.0;
21        this.zoomSpeed = 1.2;
22        this.panSpeed = 0.3;
23
24        this.noRotate = false;
25        this.noZoom = false;
26        this.noPan = false;
27        this.noRoll = false;
28
29        this.staticMoving = false;
30        this.dynamicDampingFactor = 0.2;
31
32        this.minDistance = 0;
33        this.maxDistance = Infinity;
34
35        this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
36
37        // internals
38
39        this.target = new THREE.Vector3();
40
41        var lastPosition = new THREE.Vector3();
42
43        var _state = STATE.NONE,
44        _prevState = STATE.NONE,
45
46        _eye = new THREE.Vector3(),
47
48        _rotateStart = new THREE.Vector3(),
49        _rotateEnd = new THREE.Vector3(),
50
51        _zoomStart = new THREE.Vector2(),
52        _zoomEnd = new THREE.Vector2(),
53
54        _touchZoomDistanceStart = 0,
55        _touchZoomDistanceEnd = 0,
56
57        _panStart = new THREE.Vector2(),
58        _panEnd = new THREE.Vector2();
59
60        // for reset
61
62        this.target0 = this.target.clone();
63        this.position0 = this.object.position.clone();
64        this.up0 = this.object.up.clone();
65
66        // events
67
68        var changeEvent = { type: 'change' };
69        var startEvent = { type: 'start'};
70        var endEvent = { type: 'end'};
71
72
73        // methods
74
75        this.handleResize = function () {
76
77                if ( this.domElement === document ) {
78
79                        this.screen.left = 0;
80                        this.screen.top = 0;
81                        this.screen.width = window.innerWidth;
82                        this.screen.height = window.innerHeight;
83
84                } else {
85
86                        this.screen = this.domElement.getBoundingClientRect();
87                        // adjustments come from similar code in the jquery offset() function
88                        var d = this.domElement.ownerDocument.documentElement
89                        this.screen.left += window.pageXOffset - d.clientLeft
90                        this.screen.top += window.pageYOffset - d.clientTop
91
92                }
93
94        };
95
96        this.handleEvent = function ( event ) {
97
98                if ( typeof this[ event.type ] == 'function' ) {
99
100                        this[ event.type ]( event );
101
102                }
103
104        };
105
106        this.getMouseOnScreen = function ( pageX, pageY, vector ) {
107
108                return vector.set(
109                        ( pageX - _this.screen.left ) / _this.screen.width,
110                        ( pageY - _this.screen.top ) / _this.screen.height
111                );
112
113        };
114
115        this.getMouseProjectionOnBall = (function(){
116
117                var objectUp = new THREE.Vector3(),
118                    mouseOnBall = new THREE.Vector3();
119
120
121                return function ( pageX, pageY, projection ) {
122
123                        mouseOnBall.set(
124                                ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
125                                ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5),
126                                0.0
127                        );
128
129                        var length = mouseOnBall.length();
130
131                        if ( _this.noRoll ) {
132
133                                if ( length < Math.SQRT1_2 ) {
134
135                                        mouseOnBall.z = Math.sqrt( 1.0 - length*length );
136
137                                } else {
138
139                                        mouseOnBall.z = .5 / length;
140                                       
141                                }
142
143                        } else if ( length > 1.0 ) {
144
145                                mouseOnBall.normalize();
146
147                        } else {
148
149                                mouseOnBall.z = Math.sqrt( 1.0 - length * length );
150
151                        }
152
153                        _eye.copy( _this.object.position ).sub( _this.target );
154
155                        projection.copy( _this.object.up ).setLength( mouseOnBall.y )
156                        projection.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
157                        projection.add( _eye.setLength( mouseOnBall.z ) );
158
159                        return projection;
160                }
161
162        }());
163
164        this.rotateCamera = (function(){
165
166                var axis = new THREE.Vector3(),
167                        quaternion = new THREE.Quaternion();
168
169
170                return function () {
171
172                        var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
173
174                        if ( angle ) {
175
176                                axis.crossVectors( _rotateStart, _rotateEnd ).normalize();
177
178                                angle *= _this.rotateSpeed;
179
180                                quaternion.setFromAxisAngle( axis, -angle );
181
182                                _eye.applyQuaternion( quaternion );
183                                _this.object.up.applyQuaternion( quaternion );
184
185                                _rotateEnd.applyQuaternion( quaternion );
186
187                                if ( _this.staticMoving ) {
188
189                                        _rotateStart.copy( _rotateEnd );
190
191                                } else {
192
193                                        quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
194                                        _rotateStart.applyQuaternion( quaternion );
195
196                                }
197
198                        }
199                }
200
201        }());
202
203        this.zoomCamera = function () {
204
205                if ( _state === STATE.TOUCH_ZOOM ) {
206
207                        var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
208                        _touchZoomDistanceStart = _touchZoomDistanceEnd;
209                        _eye.multiplyScalar( factor );
210
211                } else {
212
213                        var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
214
215                        if ( factor !== 1.0 && factor > 0.0 ) {
216
217                                _eye.multiplyScalar( factor );
218
219                                if ( _this.staticMoving ) {
220
221                                        _zoomStart.copy( _zoomEnd );
222
223                                } else {
224
225                                        _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
226
227                                }
228
229                        }
230
231                }
232
233        };
234
235        this.panCamera = (function(){
236
237                var mouseChange = new THREE.Vector2(),
238                        objectUp = new THREE.Vector3(),
239                        pan = new THREE.Vector3();
240
241                return function () {
242
243                        mouseChange.copy( _panEnd ).sub( _panStart );
244
245                        if ( mouseChange.lengthSq() ) {
246
247                                mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
248
249                                pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
250                                pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
251
252                                _this.object.position.add( pan );
253                                _this.target.add( pan );
254
255                                if ( _this.staticMoving ) {
256
257                                        _panStart.copy( _panEnd );
258
259                                } else {
260
261                                        _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
262
263                                }
264
265                        }
266                }
267
268        }());
269
270        this.checkDistances = function () {
271
272                if ( !_this.noZoom || !_this.noPan ) {
273
274                        if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
275
276                                _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
277
278                        }
279
280                        if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
281
282                                _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
283
284                        }
285
286                }
287
288        };
289
290        this.update = function () {
291
292                _eye.subVectors( _this.object.position, _this.target );
293
294                if ( !_this.noRotate ) {
295
296                        _this.rotateCamera();
297
298                }
299
300                if ( !_this.noZoom ) {
301
302                        _this.zoomCamera();
303
304                }
305
306                if ( !_this.noPan ) {
307
308                        _this.panCamera();
309
310                }
311
312                _this.object.position.addVectors( _this.target, _eye );
313
314                _this.checkDistances();
315
316                _this.object.lookAt( _this.target );
317
318                if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
319
320                        _this.dispatchEvent( changeEvent );
321
322                        lastPosition.copy( _this.object.position );
323
324                }
325
326        };
327
328        this.reset = function () {
329
330                _state = STATE.NONE;
331                _prevState = STATE.NONE;
332
333                _this.target.copy( _this.target0 );
334                _this.object.position.copy( _this.position0 );
335                _this.object.up.copy( _this.up0 );
336
337                _eye.subVectors( _this.object.position, _this.target );
338
339                _this.object.lookAt( _this.target );
340
341                _this.dispatchEvent( changeEvent );
342
343                lastPosition.copy( _this.object.position );
344
345        };
346
347        // listeners
348
349        function keydown( event ) {
350
351                if ( _this.enabled === false ) return;
352
353                window.removeEventListener( 'keydown', keydown );
354
355                _prevState = _state;
356
357                if ( _state !== STATE.NONE ) {
358
359                        return;
360
361                } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
362
363                        _state = STATE.ROTATE;
364
365                } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
366
367                        _state = STATE.ZOOM;
368
369                } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
370
371                        _state = STATE.PAN;
372
373                }
374
375        }
376
377        function keyup( event ) {
378
379                if ( _this.enabled === false ) return;
380
381                _state = _prevState;
382
383                window.addEventListener( 'keydown', keydown, false );
384
385        }
386
387        function mousedown( event ) {
388
389                if ( _this.enabled === false ) return;
390
391                event.preventDefault();
392                event.stopPropagation();
393
394                if ( _state === STATE.NONE ) {
395
396                        _state = event.button;
397
398                }
399
400                if ( _state === STATE.ROTATE && !_this.noRotate ) {
401
402                        _this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateStart );
403                        _rotateEnd.copy(_rotateStart)
404
405                } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
406
407                        _this.getMouseOnScreen( event.pageX, event.pageY, _zoomStart );
408                        _zoomEnd.copy(_zoomStart);
409
410                } else if ( _state === STATE.PAN && !_this.noPan ) {
411
412                        _this.getMouseOnScreen( event.pageX, event.pageY, _panStart );
413                        _panEnd.copy(_panStart)
414
415                }
416
417                document.addEventListener( 'mousemove', mousemove, false );
418                document.addEventListener( 'mouseup', mouseup, false );
419                _this.dispatchEvent( startEvent );
420
421
422        }
423
424        function mousemove( event ) {
425
426                if ( _this.enabled === false ) return;
427
428                event.preventDefault();
429                event.stopPropagation();
430
431                if ( _state === STATE.ROTATE && !_this.noRotate ) {
432
433                        _this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateEnd );
434
435                } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
436
437                        _this.getMouseOnScreen( event.pageX, event.pageY, _zoomEnd );
438
439                } else if ( _state === STATE.PAN && !_this.noPan ) {
440
441                        _this.getMouseOnScreen( event.pageX, event.pageY, _panEnd );
442
443                }
444
445        }
446
447        function mouseup( event ) {
448
449                if ( _this.enabled === false ) return;
450
451                event.preventDefault();
452                event.stopPropagation();
453
454                _state = STATE.NONE;
455
456                document.removeEventListener( 'mousemove', mousemove );
457                document.removeEventListener( 'mouseup', mouseup );
458                _this.dispatchEvent( endEvent );
459
460        }
461
462        function mousewheel( event ) {
463
464                if ( _this.enabled === false ) return;
465
466                event.preventDefault();
467                event.stopPropagation();
468
469                var delta = 0;
470
471                if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
472
473                        delta = event.wheelDelta / 40;
474
475                } else if ( event.detail ) { // Firefox
476
477                        delta = - event.detail / 3;
478
479                }
480
481                _zoomStart.y += delta * 0.01;
482                _this.dispatchEvent( startEvent );
483                _this.dispatchEvent( endEvent );
484
485        }
486
487        function touchstart( event ) {
488
489                if ( _this.enabled === false ) return;
490
491                switch ( event.touches.length ) {
492
493                        case 1:
494                                _state = STATE.TOUCH_ROTATE;
495                                _rotateEnd.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateStart ));
496                                break;
497
498                        case 2:
499                                _state = STATE.TOUCH_ZOOM;
500                                var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
501                                var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
502                                _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
503                                break;
504
505                        case 3:
506                                _state = STATE.TOUCH_PAN;
507                                _panEnd.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panStart ));
508                                break;
509
510                        default:
511                                _state = STATE.NONE;
512
513                }
514                _this.dispatchEvent( startEvent );
515
516
517        }
518
519        function touchmove( event ) {
520
521                if ( _this.enabled === false ) return;
522
523                event.preventDefault();
524                event.stopPropagation();
525
526                switch ( event.touches.length ) {
527
528                        case 1:
529                                _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd );
530                                break;
531
532                        case 2:
533                                var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
534                                var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
535                                _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
536                                break;
537
538                        case 3:
539                                _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd );
540                                break;
541
542                        default:
543                                _state = STATE.NONE;
544
545                }
546
547        }
548
549        function touchend( event ) {
550
551                if ( _this.enabled === false ) return;
552
553                switch ( event.touches.length ) {
554
555                        case 1:
556                                _rotateStart.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd ));
557                                break;
558
559                        case 2:
560                                _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
561                                break;
562
563                        case 3:
564                                _panStart.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd ));
565                                break;
566
567                }
568
569                _state = STATE.NONE;
570                _this.dispatchEvent( endEvent );
571
572        }
573
574        this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
575
576        this.domElement.addEventListener( 'mousedown', mousedown, false );
577
578        this.domElement.addEventListener( 'mousewheel', mousewheel, false );
579        this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
580
581        this.domElement.addEventListener( 'touchstart', touchstart, false );
582        this.domElement.addEventListener( 'touchend', touchend, false );
583        this.domElement.addEventListener( 'touchmove', touchmove, false );
584
585        window.addEventListener( 'keydown', keydown, false );
586        window.addEventListener( 'keyup', keyup, false );
587
588        this.handleResize();
589
590};
591
592THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
Note: See TracBrowser for help on using the repository browser.