source: js/viewer-f0/js/library/OrbitControls.js @ 208

Last change on this file since 208 was 208, checked in by mmichalski, 10 years ago

New NeuronDrawer? without U-shape in connections

  • Property svn:executable set to *
File size: 14.3 KB
Line 
1/**
2 * @author qiao / https://github.com/qiao
3 * @author mrdoob / http://mrdoob.com
4 * @author alteredq / http://alteredqualia.com/
5 * @author WestLangley / http://github.com/WestLangley
6 * @author erich666 / http://erichaines.com
7 */
8/*global THREE, console */
9
10// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
11// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
12// supported.
13//
14//    Orbit - left mouse / touch: one finger move
15//    Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
16//    Pan - right mouse, or arrow keys / touch: three finter swipe
17//
18// This is a drop-in replacement for (most) TrackballControls used in examples.
19// That is, include this js file and wherever you see:
20//      controls = new THREE.TrackballControls( camera );
21//      controls.target.z = 150;
22// Simple substitute "OrbitControls" and the control should work as-is.
23
24THREE.OrbitControls = function ( object, domElement ) {
25
26        this.object = object;
27        this.domElement = ( domElement !== undefined ) ? domElement : document;
28
29        // API
30
31        // Set to false to disable this control
32        this.enabled = true;
33
34        // "target" sets the location of focus, where the control orbits around
35        // and where it pans with respect to.
36        this.target = new THREE.Vector3();
37        // center is old, deprecated; use "target" instead
38        this.center = this.target;
39
40        // This option actually enables dollying in and out; left as "zoom" for
41        // backwards compatibility
42        this.noZoom = false;
43        this.zoomSpeed = 1.0;
44        // Limits to how far you can dolly in and out
45        this.minDistance = 0;
46        this.maxDistance = Infinity;
47
48        // Set to true to disable this control
49        this.noRotate = false;
50        this.rotateSpeed = 1.0;
51
52        // Set to true to disable this control
53        this.noPan = false;
54        this.keyPanSpeed = 7.0; // pixels moved per arrow key push
55
56        // Set to true to automatically rotate around the target
57        this.autoRotate = false;
58        this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
59
60        // How far you can orbit vertically, upper and lower limits.
61        // Range is 0 to Math.PI radians.
62        this.minPolarAngle = 0; // radians
63        this.maxPolarAngle = Math.PI; // radians
64
65        // Set to true to disable use of the keys
66        this.noKeys = false;
67        // The four arrow keys
68        this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
69
70        ////////////
71        // internals
72
73        var scope = this;
74
75        var EPS = 0.000001;
76
77        var rotateStart = new THREE.Vector2();
78        var rotateEnd = new THREE.Vector2();
79        var rotateDelta = new THREE.Vector2();
80
81        var panStart = new THREE.Vector2();
82        var panEnd = new THREE.Vector2();
83        var panDelta = new THREE.Vector2();
84
85        var dollyStart = new THREE.Vector2();
86        var dollyEnd = new THREE.Vector2();
87        var dollyDelta = new THREE.Vector2();
88
89        var phiDelta = 0;
90        var thetaDelta = 0;
91        var scale = 1;
92        var pan = new THREE.Vector3();
93
94        var lastPosition = new THREE.Vector3();
95
96        var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
97        var state = STATE.NONE;
98
99        // events
100
101        var changeEvent = { type: 'change' };
102
103
104        this.rotateLeft = function ( angle ) {
105
106                if ( angle === undefined ) {
107
108                        angle = getAutoRotationAngle();
109
110                }
111
112                thetaDelta -= angle;
113
114        };
115
116        this.rotateUp = function ( angle ) {
117
118                if ( angle === undefined ) {
119
120                        angle = getAutoRotationAngle();
121
122                }
123
124                phiDelta -= angle;
125
126        };
127
128        // pass in distance in world space to move left
129        this.panLeft = function ( distance ) {
130
131                var panOffset = new THREE.Vector3();
132                var te = this.object.matrix.elements;
133                // get X column of matrix
134                panOffset.set( te[0], te[1], te[2] );
135                panOffset.multiplyScalar(-distance);
136               
137                pan.add( panOffset );
138
139        };
140
141        // pass in distance in world space to move up
142        this.panUp = function ( distance ) {
143
144                var panOffset = new THREE.Vector3();
145                var te = this.object.matrix.elements;
146                // get Y column of matrix
147                panOffset.set( te[4], te[5], te[6] );
148                panOffset.multiplyScalar(distance);
149               
150                pan.add( panOffset );
151        };
152       
153        // main entry point; pass in Vector2 of change desired in pixel space,
154        // right and down are positive
155        this.pan = function ( delta ) {
156
157                var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
158
159                if ( scope.object.fov !== undefined ) {
160
161                        // perspective
162                        var position = scope.object.position;
163                        var offset = position.clone().sub( scope.target );
164                        var targetDistance = offset.length();
165
166                        // half of the fov is center to top of screen
167                        targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
168                        // we actually don't use screenWidth, since perspective camera is fixed to screen height
169                        scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight );
170                        scope.panUp( 2 * delta.y * targetDistance / element.clientHeight );
171
172                } else if ( scope.object.top !== undefined ) {
173
174                        // orthographic
175                        scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth );
176                        scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight );
177
178                } else {
179
180                        // camera neither orthographic or perspective - warn user
181                        console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
182
183                }
184
185        };
186
187        this.dollyIn = function ( dollyScale ) {
188
189                if ( dollyScale === undefined ) {
190
191                        dollyScale = getZoomScale();
192
193                }
194
195                scale /= dollyScale;
196
197        };
198
199        this.dollyOut = function ( dollyScale ) {
200
201                if ( dollyScale === undefined ) {
202
203                        dollyScale = getZoomScale();
204
205                }
206
207                scale *= dollyScale;
208
209        };
210
211        this.update = function () {
212
213                var position = this.object.position;
214                var offset = position.clone().sub( this.target );
215
216                // angle from z-axis around y-axis
217
218                var theta = Math.atan2( offset.x, offset.z );
219
220                // angle from y-axis
221
222                var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
223
224                if ( this.autoRotate ) {
225
226                        this.rotateLeft( getAutoRotationAngle() );
227
228                }
229
230                theta += thetaDelta;
231                phi += phiDelta;
232
233                // restrict phi to be between desired limits
234                phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
235
236                // restrict phi to be betwee EPS and PI-EPS
237                phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
238
239                var radius = offset.length() * scale;
240
241                // restrict radius to be between desired limits
242                radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
243               
244                // move target to panned location
245                this.target.add( pan );
246
247                offset.x = radius * Math.sin( phi ) * Math.sin( theta );
248                offset.y = radius * Math.cos( phi );
249                offset.z = radius * Math.sin( phi ) * Math.cos( theta );
250
251                position.copy( this.target ).add( offset );
252
253                this.object.lookAt( this.target );
254
255                thetaDelta = 0;
256                phiDelta = 0;
257                scale = 1;
258                pan.set(0,0,0);
259
260                if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
261
262                        this.dispatchEvent( changeEvent );
263
264                        lastPosition.copy( this.object.position );
265
266                }
267
268        };
269
270
271        function getAutoRotationAngle() {
272
273                return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
274
275        }
276
277        function getZoomScale() {
278
279                return Math.pow( 0.95, scope.zoomSpeed );
280
281        }
282
283        function onMouseDown( event ) {
284
285                if ( scope.enabled === false ) { return; }
286                event.preventDefault();
287
288                if ( event.button === 0 ) {
289                        if ( scope.noRotate === true ) { return; }
290
291                        state = STATE.ROTATE;
292
293                        rotateStart.set( event.clientX, event.clientY );
294
295                } else if ( event.button === 1 ) {
296                        if ( scope.noZoom === true ) { return; }
297
298                        state = STATE.DOLLY;
299
300                        dollyStart.set( event.clientX, event.clientY );
301
302                } else if ( event.button === 2 ) {
303                        if ( scope.noPan === true ) { return; }
304
305                        state = STATE.PAN;
306
307                        panStart.set( event.clientX, event.clientY );
308
309                }
310
311                // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
312                scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
313                scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
314
315        }
316
317        function onMouseMove( event ) {
318
319                if ( scope.enabled === false ) return;
320
321                event.preventDefault();
322
323                var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
324
325                if ( state === STATE.ROTATE ) {
326
327                        if ( scope.noRotate === true ) return;
328
329                        rotateEnd.set( event.clientX, event.clientY );
330                        rotateDelta.subVectors( rotateEnd, rotateStart );
331
332                        // rotating across whole screen goes 360 degrees around
333                        scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
334                        // rotating up and down along whole screen attempts to go 360, but limited to 180
335                        scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
336
337                        rotateStart.copy( rotateEnd );
338
339                } else if ( state === STATE.DOLLY ) {
340
341                        if ( scope.noZoom === true ) return;
342
343                        dollyEnd.set( event.clientX, event.clientY );
344                        dollyDelta.subVectors( dollyEnd, dollyStart );
345
346                        if ( dollyDelta.y > 0 ) {
347
348                                scope.dollyIn();
349
350                        } else {
351
352                                scope.dollyOut();
353
354                        }
355
356                        dollyStart.copy( dollyEnd );
357
358                } else if ( state === STATE.PAN ) {
359
360                        if ( scope.noPan === true ) return;
361
362                        panEnd.set( event.clientX, event.clientY );
363                        panDelta.subVectors( panEnd, panStart );
364                       
365                        scope.pan( panDelta );
366
367                        panStart.copy( panEnd );
368
369                }
370
371                // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
372                scope.update();
373
374        }
375
376        function onMouseUp( /* event */ ) {
377
378                if ( scope.enabled === false ) return;
379
380                // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
381                scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
382                scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
383
384                state = STATE.NONE;
385
386        }
387
388        function onMouseWheel( event ) {
389
390                if ( scope.enabled === false || scope.noZoom === true ) return;
391
392                var delta = 0;
393
394                if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
395
396                        delta = event.wheelDelta;
397
398                } else if ( event.detail ) { // Firefox
399
400                        delta = - event.detail;
401
402                }
403
404                if ( delta > 0 ) {
405
406                        scope.dollyOut();
407
408                } else {
409
410                        scope.dollyIn();
411
412                }
413
414        }
415
416        function onKeyDown( event ) {
417
418                if ( scope.enabled === false ) { return; }
419                if ( scope.noKeys === true ) { return; }
420                if ( scope.noPan === true ) { return; }
421
422                // pan a pixel - I guess for precise positioning?
423                // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
424                var needUpdate = false;
425               
426                switch ( event.keyCode ) {
427
428                        case scope.keys.UP:
429                                scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) );
430                                needUpdate = true;
431                                break;
432                        case scope.keys.BOTTOM:
433                                scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) );
434                                needUpdate = true;
435                                break;
436                        case scope.keys.LEFT:
437                                scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) );
438                                needUpdate = true;
439                                break;
440                        case scope.keys.RIGHT:
441                                scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) );
442                                needUpdate = true;
443                                break;
444                }
445
446                // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
447                if ( needUpdate ) {
448
449                        scope.update();
450
451                }
452
453        }
454       
455        function touchstart( event ) {
456
457                if ( scope.enabled === false ) { return; }
458
459                switch ( event.touches.length ) {
460
461                        case 1: // one-fingered touch: rotate
462                                if ( scope.noRotate === true ) { return; }
463
464                                state = STATE.TOUCH_ROTATE;
465
466                                rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
467                                break;
468
469                        case 2: // two-fingered touch: dolly
470                                if ( scope.noZoom === true ) { return; }
471
472                                state = STATE.TOUCH_DOLLY;
473
474                                var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
475                                var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
476                                var distance = Math.sqrt( dx * dx + dy * dy );
477                                dollyStart.set( 0, distance );
478                                break;
479
480                        case 3: // three-fingered touch: pan
481                                if ( scope.noPan === true ) { return; }
482
483                                state = STATE.TOUCH_PAN;
484
485                                panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
486                                break;
487
488                        default:
489                                state = STATE.NONE;
490
491                }
492        }
493
494        function touchmove( event ) {
495
496                if ( scope.enabled === false ) { return; }
497
498                event.preventDefault();
499                event.stopPropagation();
500
501                var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
502
503                switch ( event.touches.length ) {
504
505                        case 1: // one-fingered touch: rotate
506                                if ( scope.noRotate === true ) { return; }
507                                if ( state !== STATE.TOUCH_ROTATE ) { return; }
508
509                                rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
510                                rotateDelta.subVectors( rotateEnd, rotateStart );
511
512                                // rotating across whole screen goes 360 degrees around
513                                scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
514                                // rotating up and down along whole screen attempts to go 360, but limited to 180
515                                scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
516
517                                rotateStart.copy( rotateEnd );
518                                break;
519
520                        case 2: // two-fingered touch: dolly
521                                if ( scope.noZoom === true ) { return; }
522                                if ( state !== STATE.TOUCH_DOLLY ) { return; }
523
524                                var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
525                                var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
526                                var distance = Math.sqrt( dx * dx + dy * dy );
527
528                                dollyEnd.set( 0, distance );
529                                dollyDelta.subVectors( dollyEnd, dollyStart );
530
531                                if ( dollyDelta.y > 0 ) {
532
533                                        scope.dollyOut();
534
535                                } else {
536
537                                        scope.dollyIn();
538
539                                }
540
541                                dollyStart.copy( dollyEnd );
542                                break;
543
544                        case 3: // three-fingered touch: pan
545                                if ( scope.noPan === true ) { return; }
546                                if ( state !== STATE.TOUCH_PAN ) { return; }
547
548                                panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
549                                panDelta.subVectors( panEnd, panStart );
550                               
551                                scope.pan( panDelta );
552
553                                panStart.copy( panEnd );
554                                break;
555
556                        default:
557                                state = STATE.NONE;
558
559                }
560
561        }
562
563        function touchend( /* event */ ) {
564
565                if ( scope.enabled === false ) { return; }
566
567                state = STATE.NONE;
568        }
569
570        this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
571        this.domElement.addEventListener( 'mousedown', onMouseDown, false );
572        this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
573        this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
574
575        this.domElement.addEventListener( 'keydown', onKeyDown, false );
576
577        this.domElement.addEventListener( 'touchstart', touchstart, false );
578        this.domElement.addEventListener( 'touchend', touchend, false );
579        this.domElement.addEventListener( 'touchmove', touchmove, false );
580
581};
582
583THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
Note: See TracBrowser for help on using the repository browser.