PageSourceSearch

https://zehaozhu.github.io/ContactArt/explorer/js/OrthographicTrackballControls.js

js zehaozhu.github.io collected 2026-10-03 10:14:26 UTC 16,564 bytes, 626 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.