Thu, 06 Apr 2017 17:18:24 +0200
finishing first release
16 | 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 | ||
9 | // This set of controls performs orbiting, dollying (zooming), and panning. | |
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). | |
11 | // | |
12 | // Orbit - left mouse / touch: one finger move | |
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish | |
14 | // Pan - right mouse, or arrow keys / touch: three finger swipe | |
15 | ||
16 | THREE.OrbitControls = function ( object, domElement ) { | |
17 | ||
18 | this.object = object; | |
19 | ||
20 | this.domElement = ( domElement !== undefined ) ? domElement : document; | |
21 | ||
22 | // Set to false to disable this control | |
23 | this.enabled = true; | |
24 | ||
25 | // "target" sets the location of focus, where the object orbits around | |
26 | this.target = new THREE.Vector3(); | |
27 | ||
28 | // How far you can dolly in and out ( PerspectiveCamera only ) | |
29 | this.minDistance = 0; | |
30 | this.maxDistance = Infinity; | |
31 | ||
32 | // How far you can zoom in and out ( OrthographicCamera only ) | |
33 | this.minZoom = 0; | |
34 | this.maxZoom = Infinity; | |
35 | ||
36 | // How far you can orbit vertically, upper and lower limits. | |
37 | // Range is 0 to Math.PI radians. | |
38 | this.minPolarAngle = 0; // radians | |
39 | this.maxPolarAngle = Math.PI; // radians | |
40 | ||
41 | // How far you can orbit horizontally, upper and lower limits. | |
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. | |
43 | this.minAzimuthAngle = - Infinity; // radians | |
44 | this.maxAzimuthAngle = Infinity; // radians | |
45 | ||
46 | // Set to true to enable damping (inertia) | |
47 | // If damping is enabled, you must call controls.update() in your animation loop | |
48 | this.enableDamping = false; | |
49 | this.dampingFactor = 0.25; | |
50 | ||
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. | |
52 | // Set to false to disable zooming | |
53 | this.enableZoom = true; | |
54 | this.zoomSpeed = 1.0; | |
55 | ||
56 | // Set to false to disable rotating | |
57 | this.enableRotate = true; | |
58 | this.rotateSpeed = 1.0; | |
59 | ||
60 | // Set to false to disable panning | |
61 | this.enablePan = true; | |
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push | |
63 | ||
64 | // Set to true to automatically rotate around the target | |
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop | |
66 | this.autoRotate = false; | |
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 | |
68 | ||
69 | // Set to false to disable use of the keys | |
70 | this.enableKeys = true; | |
71 | ||
72 | // The four arrow keys | |
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; | |
74 | ||
75 | // Mouse buttons | |
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; | |
77 | ||
78 | // for reset | |
79 | this.target0 = this.target.clone(); | |
80 | this.position0 = this.object.position.clone(); | |
81 | this.zoom0 = this.object.zoom; | |
82 | ||
83 | // | |
84 | // public methods | |
85 | // | |
86 | ||
87 | this.getPolarAngle = function () { | |
88 | ||
89 | return spherical.phi; | |
90 | ||
91 | }; | |
92 | ||
93 | this.getAzimuthalAngle = function () { | |
94 | ||
95 | return spherical.theta; | |
96 | ||
97 | }; | |
98 | ||
99 | this.reset = function () { | |
100 | ||
101 | scope.target.copy( scope.target0 ); | |
102 | scope.object.position.copy( scope.position0 ); | |
103 | scope.object.zoom = scope.zoom0; | |
104 | ||
105 | scope.object.updateProjectionMatrix(); | |
106 | scope.dispatchEvent( changeEvent ); | |
107 | ||
108 | scope.update(); | |
109 | ||
110 | state = STATE.NONE; | |
111 | ||
112 | }; | |
113 | ||
114 | // this method is exposed, but perhaps it would be better if we can make it private... | |
115 | this.update = function () { | |
116 | ||
117 | var offset = new THREE.Vector3(); | |
118 | ||
119 | // so camera.up is the orbit axis | |
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); | |
121 | var quatInverse = quat.clone().inverse(); | |
122 | ||
123 | var lastPosition = new THREE.Vector3(); | |
124 | var lastQuaternion = new THREE.Quaternion(); | |
125 | ||
126 | return function update() { | |
127 | ||
128 | var position = scope.object.position; | |
129 | ||
130 | offset.copy( position ).sub( scope.target ); | |
131 | ||
132 | // rotate offset to "y-axis-is-up" space | |
133 | offset.applyQuaternion( quat ); | |
134 | ||
135 | // angle from z-axis around y-axis | |
136 | spherical.setFromVector3( offset ); | |
137 | ||
138 | if ( scope.autoRotate && state === STATE.NONE ) { | |
139 | ||
140 | rotateLeft( getAutoRotationAngle() ); | |
141 | ||
142 | } | |
143 | ||
144 | spherical.theta += sphericalDelta.theta; | |
145 | spherical.phi += sphericalDelta.phi; | |
146 | ||
147 | // restrict theta to be between desired limits | |
148 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); | |
149 | ||
150 | // restrict phi to be between desired limits | |
151 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); | |
152 | ||
153 | spherical.makeSafe(); | |
154 | ||
155 | ||
156 | spherical.radius *= scale; | |
157 | ||
158 | // restrict radius to be between desired limits | |
159 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); | |
160 | ||
161 | // move target to panned location | |
162 | scope.target.add( panOffset ); | |
163 | ||
164 | offset.setFromSpherical( spherical ); | |
165 | ||
166 | // rotate offset back to "camera-up-vector-is-up" space | |
167 | offset.applyQuaternion( quatInverse ); | |
168 | ||
169 | position.copy( scope.target ).add( offset ); | |
170 | ||
171 | scope.object.lookAt( scope.target ); | |
172 | ||
173 | if ( scope.enableDamping === true ) { | |
174 | ||
175 | sphericalDelta.theta *= ( 1 - scope.dampingFactor ); | |
176 | sphericalDelta.phi *= ( 1 - scope.dampingFactor ); | |
177 | ||
178 | } else { | |
179 | ||
180 | sphericalDelta.set( 0, 0, 0 ); | |
181 | ||
182 | } | |
183 | ||
184 | scale = 1; | |
185 | panOffset.set( 0, 0, 0 ); | |
186 | ||
187 | // update condition is: | |
188 | // min(camera displacement, camera rotation in radians)^2 > EPS | |
189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 | |
190 | ||
191 | if ( zoomChanged || | |
192 | lastPosition.distanceToSquared( scope.object.position ) > EPS || | |
193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { | |
194 | ||
195 | scope.dispatchEvent( changeEvent ); | |
196 | ||
197 | lastPosition.copy( scope.object.position ); | |
198 | lastQuaternion.copy( scope.object.quaternion ); | |
199 | zoomChanged = false; | |
200 | ||
201 | return true; | |
202 | ||
203 | } | |
204 | ||
205 | return false; | |
206 | ||
207 | }; | |
208 | ||
209 | }(); | |
210 | ||
211 | this.dispose = function () { | |
212 | ||
213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); | |
214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); | |
215 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); | |
216 | ||
217 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); | |
218 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); | |
219 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); | |
220 | ||
221 | document.removeEventListener( 'mousemove', onMouseMove, false ); | |
222 | document.removeEventListener( 'mouseup', onMouseUp, false ); | |
223 | ||
224 | window.removeEventListener( 'keydown', onKeyDown, false ); | |
225 | ||
226 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? | |
227 | ||
228 | }; | |
229 | ||
230 | // | |
231 | // internals | |
232 | // | |
233 | ||
234 | var scope = this; | |
235 | ||
236 | var changeEvent = { type: 'change' }; | |
237 | var startEvent = { type: 'start' }; | |
238 | var endEvent = { type: 'end' }; | |
239 | ||
240 | var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 }; | |
241 | ||
242 | var state = STATE.NONE; | |
243 | ||
244 | var EPS = 0.000001; | |
245 | ||
246 | // current position in spherical coordinates | |
247 | var spherical = new THREE.Spherical(); | |
248 | var sphericalDelta = new THREE.Spherical(); | |
249 | ||
250 | var scale = 1; | |
251 | var panOffset = new THREE.Vector3(); | |
252 | var zoomChanged = false; | |
253 | ||
254 | var rotateStart = new THREE.Vector2(); | |
255 | var rotateEnd = new THREE.Vector2(); | |
256 | var rotateDelta = new THREE.Vector2(); | |
257 | ||
258 | var panStart = new THREE.Vector2(); | |
259 | var panEnd = new THREE.Vector2(); | |
260 | var panDelta = new THREE.Vector2(); | |
261 | ||
262 | var dollyStart = new THREE.Vector2(); | |
263 | var dollyEnd = new THREE.Vector2(); | |
264 | var dollyDelta = new THREE.Vector2(); | |
265 | ||
266 | function getAutoRotationAngle() { | |
267 | ||
268 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; | |
269 | ||
270 | } | |
271 | ||
272 | function getZoomScale() { | |
273 | ||
274 | return Math.pow( 0.95, scope.zoomSpeed ); | |
275 | ||
276 | } | |
277 | ||
278 | function rotateLeft( angle ) { | |
279 | ||
280 | sphericalDelta.theta -= angle; | |
281 | ||
282 | } | |
283 | ||
284 | function rotateUp( angle ) { | |
285 | ||
286 | sphericalDelta.phi -= angle; | |
287 | ||
288 | } | |
289 | ||
290 | var panLeft = function () { | |
291 | ||
292 | var v = new THREE.Vector3(); | |
293 | ||
294 | return function panLeft( distance, objectMatrix ) { | |
295 | ||
296 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix | |
297 | v.multiplyScalar( - distance ); | |
298 | ||
299 | panOffset.add( v ); | |
300 | ||
301 | }; | |
302 | ||
303 | }(); | |
304 | ||
305 | var panUp = function () { | |
306 | ||
307 | var v = new THREE.Vector3(); | |
308 | ||
309 | return function panUp( distance, objectMatrix ) { | |
310 | ||
311 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix | |
312 | v.multiplyScalar( distance ); | |
313 | ||
314 | panOffset.add( v ); | |
315 | ||
316 | }; | |
317 | ||
318 | }(); | |
319 | ||
320 | // deltaX and deltaY are in pixels; right and down are positive | |
321 | var pan = function () { | |
322 | ||
323 | var offset = new THREE.Vector3(); | |
324 | ||
325 | return function pan( deltaX, deltaY ) { | |
326 | ||
327 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; | |
328 | ||
329 | if ( scope.object instanceof THREE.PerspectiveCamera ) { | |
330 | ||
331 | // perspective | |
332 | var position = scope.object.position; | |
333 | offset.copy( position ).sub( scope.target ); | |
334 | var targetDistance = offset.length(); | |
335 | ||
336 | // half of the fov is center to top of screen | |
337 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); | |
338 | ||
339 | // we actually don't use screenWidth, since perspective camera is fixed to screen height | |
340 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); | |
341 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); | |
342 | ||
343 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { | |
344 | ||
345 | // orthographic | |
346 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); | |
347 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); | |
348 | ||
349 | } else { | |
350 | ||
351 | // camera neither orthographic nor perspective | |
352 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); | |
353 | scope.enablePan = false; | |
354 | ||
355 | } | |
356 | ||
357 | }; | |
358 | ||
359 | }(); | |
360 | ||
361 | function dollyIn( dollyScale ) { | |
362 | ||
363 | if ( scope.object instanceof THREE.PerspectiveCamera ) { | |
364 | ||
365 | scale /= dollyScale; | |
366 | ||
367 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { | |
368 | ||
369 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); | |
370 | scope.object.updateProjectionMatrix(); | |
371 | zoomChanged = true; | |
372 | ||
373 | } else { | |
374 | ||
375 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); | |
376 | scope.enableZoom = false; | |
377 | ||
378 | } | |
379 | ||
380 | } | |
381 | ||
382 | function dollyOut( dollyScale ) { | |
383 | ||
384 | if ( scope.object instanceof THREE.PerspectiveCamera ) { | |
385 | ||
386 | scale *= dollyScale; | |
387 | ||
388 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { | |
389 | ||
390 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); | |
391 | scope.object.updateProjectionMatrix(); | |
392 | zoomChanged = true; | |
393 | ||
394 | } else { | |
395 | ||
396 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); | |
397 | scope.enableZoom = false; | |
398 | ||
399 | } | |
400 | ||
401 | } | |
402 | ||
403 | // | |
404 | // event callbacks - update the object state | |
405 | // | |
406 | ||
407 | function handleMouseDownRotate( event ) { | |
408 | ||
409 | //console.log( 'handleMouseDownRotate' ); | |
410 | ||
411 | rotateStart.set( event.clientX, event.clientY ); | |
412 | ||
413 | } | |
414 | ||
415 | function handleMouseDownDolly( event ) { | |
416 | ||
417 | //console.log( 'handleMouseDownDolly' ); | |
418 | ||
419 | dollyStart.set( event.clientX, event.clientY ); | |
420 | ||
421 | } | |
422 | ||
423 | function handleMouseDownPan( event ) { | |
424 | ||
425 | //console.log( 'handleMouseDownPan' ); | |
426 | ||
427 | panStart.set( event.clientX, event.clientY ); | |
428 | ||
429 | } | |
430 | ||
431 | function handleMouseMoveRotate( event ) { | |
432 | ||
433 | //console.log( 'handleMouseMoveRotate' ); | |
434 | ||
435 | rotateEnd.set( event.clientX, event.clientY ); | |
436 | rotateDelta.subVectors( rotateEnd, rotateStart ); | |
437 | ||
438 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; | |
439 | ||
440 | // rotating across whole screen goes 360 degrees around | |
441 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); | |
442 | ||
443 | // rotating up and down along whole screen attempts to go 360, but limited to 180 | |
444 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); | |
445 | ||
446 | rotateStart.copy( rotateEnd ); | |
447 | ||
448 | scope.update(); | |
449 | ||
450 | } | |
451 | ||
452 | function handleMouseMoveDolly( event ) { | |
453 | ||
454 | //console.log( 'handleMouseMoveDolly' ); | |
455 | ||
456 | dollyEnd.set( event.clientX, event.clientY ); | |
457 | ||
458 | dollyDelta.subVectors( dollyEnd, dollyStart ); | |
459 | ||
460 | if ( dollyDelta.y > 0 ) { | |
461 | ||
462 | dollyIn( getZoomScale() ); | |
463 | ||
464 | } else if ( dollyDelta.y < 0 ) { | |
465 | ||
466 | dollyOut( getZoomScale() ); | |
467 | ||
468 | } | |
469 | ||
470 | dollyStart.copy( dollyEnd ); | |
471 | ||
472 | scope.update(); | |
473 | ||
474 | } | |
475 | ||
476 | function handleMouseMovePan( event ) { | |
477 | ||
478 | //console.log( 'handleMouseMovePan' ); | |
479 | ||
480 | panEnd.set( event.clientX, event.clientY ); | |
481 | ||
482 | panDelta.subVectors( panEnd, panStart ); | |
483 | ||
484 | pan( panDelta.x, panDelta.y ); | |
485 | ||
486 | panStart.copy( panEnd ); | |
487 | ||
488 | scope.update(); | |
489 | ||
490 | } | |
491 | ||
492 | function handleMouseUp( event ) { | |
493 | ||
494 | // console.log( 'handleMouseUp' ); | |
495 | ||
496 | } | |
497 | ||
498 | function handleMouseWheel( event ) { | |
499 | ||
500 | // console.log( 'handleMouseWheel' ); | |
501 | ||
502 | if ( event.deltaY < 0 ) { | |
503 | ||
504 | dollyOut( getZoomScale() ); | |
505 | ||
506 | } else if ( event.deltaY > 0 ) { | |
507 | ||
508 | dollyIn( getZoomScale() ); | |
509 | ||
510 | } | |
511 | ||
512 | scope.update(); | |
513 | ||
514 | } | |
515 | ||
516 | function handleKeyDown( event ) { | |
517 | ||
518 | //console.log( 'handleKeyDown' ); | |
519 | ||
520 | switch ( event.keyCode ) { | |
521 | ||
522 | case scope.keys.UP: | |
523 | pan( 0, scope.keyPanSpeed ); | |
524 | scope.update(); | |
525 | break; | |
526 | ||
527 | case scope.keys.BOTTOM: | |
528 | pan( 0, - scope.keyPanSpeed ); | |
529 | scope.update(); | |
530 | break; | |
531 | ||
532 | case scope.keys.LEFT: | |
533 | pan( scope.keyPanSpeed, 0 ); | |
534 | scope.update(); | |
535 | break; | |
536 | ||
537 | case scope.keys.RIGHT: | |
538 | pan( - scope.keyPanSpeed, 0 ); | |
539 | scope.update(); | |
540 | break; | |
541 | ||
542 | } | |
543 | ||
544 | } | |
545 | ||
546 | function handleTouchStartRotate( event ) { | |
547 | ||
548 | //console.log( 'handleTouchStartRotate' ); | |
549 | ||
550 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
551 | ||
552 | } | |
553 | ||
554 | function handleTouchStartDolly( event ) { | |
555 | ||
556 | //console.log( 'handleTouchStartDolly' ); | |
557 | ||
558 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; | |
559 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; | |
560 | ||
561 | var distance = Math.sqrt( dx * dx + dy * dy ); | |
562 | ||
563 | dollyStart.set( 0, distance ); | |
564 | ||
565 | } | |
566 | ||
567 | function handleTouchStartPan( event ) { | |
568 | ||
569 | //console.log( 'handleTouchStartPan' ); | |
570 | ||
571 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
572 | ||
573 | } | |
574 | ||
575 | function handleTouchMoveRotate( event ) { | |
576 | ||
577 | //console.log( 'handleTouchMoveRotate' ); | |
578 | ||
579 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
580 | rotateDelta.subVectors( rotateEnd, rotateStart ); | |
581 | ||
582 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; | |
583 | ||
584 | // rotating across whole screen goes 360 degrees around | |
585 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); | |
586 | ||
587 | // rotating up and down along whole screen attempts to go 360, but limited to 180 | |
588 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); | |
589 | ||
590 | rotateStart.copy( rotateEnd ); | |
591 | ||
592 | scope.update(); | |
593 | ||
594 | } | |
595 | ||
596 | function handleTouchMoveDolly( event ) { | |
597 | ||
598 | //console.log( 'handleTouchMoveDolly' ); | |
599 | ||
600 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; | |
601 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; | |
602 | ||
603 | var distance = Math.sqrt( dx * dx + dy * dy ); | |
604 | ||
605 | dollyEnd.set( 0, distance ); | |
606 | ||
607 | dollyDelta.subVectors( dollyEnd, dollyStart ); | |
608 | ||
609 | if ( dollyDelta.y > 0 ) { | |
610 | ||
611 | dollyOut( getZoomScale() ); | |
612 | ||
613 | } else if ( dollyDelta.y < 0 ) { | |
614 | ||
615 | dollyIn( getZoomScale() ); | |
616 | ||
617 | } | |
618 | ||
619 | dollyStart.copy( dollyEnd ); | |
620 | ||
621 | scope.update(); | |
622 | ||
623 | } | |
624 | ||
625 | function handleTouchMovePan( event ) { | |
626 | ||
627 | //console.log( 'handleTouchMovePan' ); | |
628 | ||
629 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
630 | ||
631 | panDelta.subVectors( panEnd, panStart ); | |
632 | ||
633 | pan( panDelta.x, panDelta.y ); | |
634 | ||
635 | panStart.copy( panEnd ); | |
636 | ||
637 | scope.update(); | |
638 | ||
639 | } | |
640 | ||
641 | function handleTouchEnd( event ) { | |
642 | ||
643 | //console.log( 'handleTouchEnd' ); | |
644 | ||
645 | } | |
646 | ||
647 | // | |
648 | // event handlers - FSM: listen for events and reset state | |
649 | // | |
650 | ||
651 | function onMouseDown( event ) { | |
652 | ||
653 | if ( scope.enabled === false ) return; | |
654 | ||
655 | event.preventDefault(); | |
656 | ||
657 | if ( event.button === scope.mouseButtons.ORBIT ) { | |
658 | ||
659 | if ( scope.enableRotate === false ) return; | |
660 | ||
661 | handleMouseDownRotate( event ); | |
662 | ||
663 | state = STATE.ROTATE; | |
664 | ||
665 | } else if ( event.button === scope.mouseButtons.ZOOM ) { | |
666 | ||
667 | if ( scope.enableZoom === false ) return; | |
668 | ||
669 | handleMouseDownDolly( event ); | |
670 | ||
671 | state = STATE.DOLLY; | |
672 | ||
673 | } else if ( event.button === scope.mouseButtons.PAN ) { | |
674 | ||
675 | if ( scope.enablePan === false ) return; | |
676 | ||
677 | handleMouseDownPan( event ); | |
678 | ||
679 | state = STATE.PAN; | |
680 | ||
681 | } | |
682 | ||
683 | if ( state !== STATE.NONE ) { | |
684 | ||
685 | document.addEventListener( 'mousemove', onMouseMove, false ); | |
686 | document.addEventListener( 'mouseup', onMouseUp, false ); | |
687 | ||
688 | scope.dispatchEvent( startEvent ); | |
689 | ||
690 | } | |
691 | ||
692 | } | |
693 | ||
694 | function onMouseMove( event ) { | |
695 | ||
696 | if ( scope.enabled === false ) return; | |
697 | ||
698 | event.preventDefault(); | |
699 | ||
700 | if ( state === STATE.ROTATE ) { | |
701 | ||
702 | if ( scope.enableRotate === false ) return; | |
703 | ||
704 | handleMouseMoveRotate( event ); | |
705 | ||
706 | } else if ( state === STATE.DOLLY ) { | |
707 | ||
708 | if ( scope.enableZoom === false ) return; | |
709 | ||
710 | handleMouseMoveDolly( event ); | |
711 | ||
712 | } else if ( state === STATE.PAN ) { | |
713 | ||
714 | if ( scope.enablePan === false ) return; | |
715 | ||
716 | handleMouseMovePan( event ); | |
717 | ||
718 | } | |
719 | ||
720 | } | |
721 | ||
722 | function onMouseUp( event ) { | |
723 | ||
724 | if ( scope.enabled === false ) return; | |
725 | ||
726 | handleMouseUp( event ); | |
727 | ||
728 | document.removeEventListener( 'mousemove', onMouseMove, false ); | |
729 | document.removeEventListener( 'mouseup', onMouseUp, false ); | |
730 | ||
731 | scope.dispatchEvent( endEvent ); | |
732 | ||
733 | state = STATE.NONE; | |
734 | ||
735 | } | |
736 | ||
737 | function onMouseWheel( event ) { | |
738 | ||
739 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; | |
740 | ||
741 | event.preventDefault(); | |
742 | event.stopPropagation(); | |
743 | ||
744 | handleMouseWheel( event ); | |
745 | ||
746 | scope.dispatchEvent( startEvent ); // not sure why these are here... | |
747 | scope.dispatchEvent( endEvent ); | |
748 | ||
749 | } | |
750 | ||
751 | function onKeyDown( event ) { | |
752 | ||
753 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; | |
754 | ||
755 | handleKeyDown( event ); | |
756 | ||
757 | } | |
758 | ||
759 | function onTouchStart( event ) { | |
760 | ||
761 | if ( scope.enabled === false ) return; | |
762 | ||
763 | switch ( event.touches.length ) { | |
764 | ||
765 | case 1: // one-fingered touch: rotate | |
766 | ||
767 | if ( scope.enableRotate === false ) return; | |
768 | ||
769 | handleTouchStartRotate( event ); | |
770 | ||
771 | state = STATE.TOUCH_ROTATE; | |
772 | ||
773 | break; | |
774 | ||
775 | case 2: // two-fingered touch: dolly | |
776 | ||
777 | if ( scope.enableZoom === false ) return; | |
778 | ||
779 | handleTouchStartDolly( event ); | |
780 | ||
781 | state = STATE.TOUCH_DOLLY; | |
782 | ||
783 | break; | |
784 | ||
785 | case 3: // three-fingered touch: pan | |
786 | ||
787 | if ( scope.enablePan === false ) return; | |
788 | ||
789 | handleTouchStartPan( event ); | |
790 | ||
791 | state = STATE.TOUCH_PAN; | |
792 | ||
793 | break; | |
794 | ||
795 | default: | |
796 | ||
797 | state = STATE.NONE; | |
798 | ||
799 | } | |
800 | ||
801 | if ( state !== STATE.NONE ) { | |
802 | ||
803 | scope.dispatchEvent( startEvent ); | |
804 | ||
805 | } | |
806 | ||
807 | } | |
808 | ||
809 | function onTouchMove( event ) { | |
810 | ||
811 | if ( scope.enabled === false ) return; | |
812 | ||
813 | event.preventDefault(); | |
814 | event.stopPropagation(); | |
815 | ||
816 | switch ( event.touches.length ) { | |
817 | ||
818 | case 1: // one-fingered touch: rotate | |
819 | ||
820 | if ( scope.enableRotate === false ) return; | |
821 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?... | |
822 | ||
823 | handleTouchMoveRotate( event ); | |
824 | ||
825 | break; | |
826 | ||
827 | case 2: // two-fingered touch: dolly | |
828 | ||
829 | if ( scope.enableZoom === false ) return; | |
830 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?... | |
831 | ||
832 | handleTouchMoveDolly( event ); | |
833 | ||
834 | break; | |
835 | ||
836 | case 3: // three-fingered touch: pan | |
837 | ||
838 | if ( scope.enablePan === false ) return; | |
839 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?... | |
840 | ||
841 | handleTouchMovePan( event ); | |
842 | ||
843 | break; | |
844 | ||
845 | default: | |
846 | ||
847 | state = STATE.NONE; | |
848 | ||
849 | } | |
850 | ||
851 | } | |
852 | ||
853 | function onTouchEnd( event ) { | |
854 | ||
855 | if ( scope.enabled === false ) return; | |
856 | ||
857 | handleTouchEnd( event ); | |
858 | ||
859 | scope.dispatchEvent( endEvent ); | |
860 | ||
861 | state = STATE.NONE; | |
862 | ||
863 | } | |
864 | ||
865 | function onContextMenu( event ) { | |
866 | ||
867 | event.preventDefault(); | |
868 | ||
869 | } | |
870 | ||
871 | // | |
872 | ||
873 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); | |
874 | ||
875 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); | |
876 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); | |
877 | ||
878 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); | |
879 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); | |
880 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); | |
881 | ||
882 | window.addEventListener( 'keydown', onKeyDown, false ); | |
883 | ||
884 | // force an update at start | |
885 | ||
886 | this.update(); | |
887 | ||
888 | }; | |
889 | ||
890 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); | |
891 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; | |
892 | ||
893 | Object.defineProperties( THREE.OrbitControls.prototype, { | |
894 | ||
895 | center: { | |
896 | ||
897 | get: function () { | |
898 | ||
899 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); | |
900 | return this.target; | |
901 | ||
902 | } | |
903 | ||
904 | }, | |
905 | ||
906 | // backward compatibility | |
907 | ||
908 | noZoom: { | |
909 | ||
910 | get: function () { | |
911 | ||
912 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); | |
913 | return ! this.enableZoom; | |
914 | ||
915 | }, | |
916 | ||
917 | set: function ( value ) { | |
918 | ||
919 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); | |
920 | this.enableZoom = ! value; | |
921 | ||
922 | } | |
923 | ||
924 | }, | |
925 | ||
926 | noRotate: { | |
927 | ||
928 | get: function () { | |
929 | ||
930 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); | |
931 | return ! this.enableRotate; | |
932 | ||
933 | }, | |
934 | ||
935 | set: function ( value ) { | |
936 | ||
937 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); | |
938 | this.enableRotate = ! value; | |
939 | ||
940 | } | |
941 | ||
942 | }, | |
943 | ||
944 | noPan: { | |
945 | ||
946 | get: function () { | |
947 | ||
948 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); | |
949 | return ! this.enablePan; | |
950 | ||
951 | }, | |
952 | ||
953 | set: function ( value ) { | |
954 | ||
955 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); | |
956 | this.enablePan = ! value; | |
957 | ||
958 | } | |
959 | ||
960 | }, | |
961 | ||
962 | noKeys: { | |
963 | ||
964 | get: function () { | |
965 | ||
966 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); | |
967 | return ! this.enableKeys; | |
968 | ||
969 | }, | |
970 | ||
971 | set: function ( value ) { | |
972 | ||
973 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); | |
974 | this.enableKeys = ! value; | |
975 | ||
976 | } | |
977 | ||
978 | }, | |
979 | ||
980 | staticMoving: { | |
981 | ||
982 | get: function () { | |
983 | ||
984 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); | |
985 | return ! this.enableDamping; | |
986 | ||
987 | }, | |
988 | ||
989 | set: function ( value ) { | |
990 | ||
991 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); | |
992 | this.enableDamping = ! value; | |
993 | ||
994 | } | |
995 | ||
996 | }, | |
997 | ||
998 | dynamicDampingFactor: { | |
999 | ||
1000 | get: function () { | |
1001 | ||
1002 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); | |
1003 | return this.dampingFactor; | |
1004 | ||
1005 | }, | |
1006 | ||
1007 | set: function ( value ) { | |
1008 | ||
1009 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); | |
1010 | this.dampingFactor = value; | |
1011 | ||
1012 | } | |
1013 | ||
1014 | } | |
1015 | ||
1016 | } ); |