TransformControls.js 29.9 KB
Newer Older
A
Aleksandar Rodic 已提交
1 2 3
/**
 * @author arodic / https://github.com/arodic
 */
4
 /*jshint sub:true*/
A
Aleksandar Rodic 已提交
5

6
(function () {
A
Aleksandar Rodic 已提交
7

8
	'use strict';
A
Aleksandar Rodic 已提交
9

10
	var GizmoMaterial = function ( parameters ) {
11

12
		THREE.MeshBasicMaterial.call( this );
A
Aleksandar Rodic 已提交
13

14 15 16 17
		this.depthTest = false;
		this.depthWrite = false;
		this.side = THREE.FrontSide;
		this.transparent = true;
18

19
		this.setValues( parameters );
20

21 22
		this.oldColor = this.color.clone();
		this.oldOpacity = this.opacity;
A
Aleksandar Rodic 已提交
23

24
		this.highlight = function( highlighted ) {
A
Aleksandar Rodic 已提交
25

26
			if ( highlighted ) {
A
Aleksandar Rodic 已提交
27

28 29
				this.color.setRGB( 1, 1, 0 );
				this.opacity = 1;
A
Aleksandar Rodic 已提交
30

31
			} else {
32

33 34
					this.color.copy( this.oldColor );
					this.opacity = this.oldOpacity;
35 36 37

			}

38
		};
39

40
	};
41

42
	GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype );
A
Aleksandar Rodic 已提交
43

44
	var GizmoLineMaterial = function ( parameters ) {
A
Aleksandar Rodic 已提交
45

46
		THREE.LineBasicMaterial.call( this );
A
Aleksandar Rodic 已提交
47

48 49 50 51
		this.depthTest = false;
		this.depthWrite = false;
		this.transparent = true;
		this.linewidth = 1;
A
Aleksandar Rodic 已提交
52

53
		this.setValues( parameters );
A
Aleksandar Rodic 已提交
54

55 56
		this.oldColor = this.color.clone();
		this.oldOpacity = this.opacity;
57

58
		this.highlight = function( highlighted ) {
59

60
			if ( highlighted ) {
61

62 63
				this.color.setRGB( 1, 1, 0 );
				this.opacity = 1;
64

65
			} else {
66

67 68
					this.color.copy( this.oldColor );
					this.opacity = this.oldOpacity;
A
Aleksandar Rodic 已提交
69

70
			}
71

72
		};
73

74
	};
75

76
	GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype );
77

78
	THREE.TransformGizmo = function () {
79

80 81 82
		var scope = this;
		var showPickers = false; //debug
		var showActivePlane = false; //debug
83

84
		this.init = function () {
A
Aleksandar Rodic 已提交
85

86
			THREE.Object3D.call( this );
87

88 89 90
			this.handles = new THREE.Object3D();
			this.pickers = new THREE.Object3D();
			this.planes = new THREE.Object3D();
91

92 93 94
			this.add(this.handles);
			this.add(this.pickers);
			this.add(this.planes);
95

96
			//// PLANES
97

98 99 100
			var planeGeometry = new THREE.PlaneGeometry( 50, 50, 2, 2 );
			var planeMaterial = new THREE.MeshBasicMaterial( { wireframe: true } );
			planeMaterial.side = THREE.DoubleSide;
101

102 103 104 105 106 107
			var planes = {
				"XY":   new THREE.Mesh( planeGeometry, planeMaterial ),
				"YZ":   new THREE.Mesh( planeGeometry, planeMaterial ),
				"XZ":   new THREE.Mesh( planeGeometry, planeMaterial ),
				"XYZE": new THREE.Mesh( planeGeometry, planeMaterial )
			};
108

109
			this.activePlane = planes["XYZE"];
110

111 112
			planes["YZ"].rotation.set( 0, Math.PI/2, 0 );
			planes["XZ"].rotation.set( -Math.PI/2, 0, 0 );
113

114 115 116 117 118
			for (var i in planes) {
				planes[i].name = i;
				this.planes.add(planes[i]);
				this.planes[i] = planes[i];
				planes[i].visible = false;
A
Aleksandar Rodic 已提交
119 120
			}

121
			//// HANDLES AND PICKERS
A
Aleksandar Rodic 已提交
122

123
			var setupGizmos = function( gizmoMap, parent ) {
A
Aleksandar Rodic 已提交
124

125
				for ( var name in gizmoMap ) {
A
Aleksandar Rodic 已提交
126

127
					for ( i = gizmoMap[name].length; i--;) {
128

129 130 131
						var object = gizmoMap[name][i][0];
						var position = gizmoMap[name][i][1];
						var rotation = gizmoMap[name][i][2];
A
Aleksandar Rodic 已提交
132

133
						object.name = name;
A
Aleksandar Rodic 已提交
134

135 136
						if ( position ) object.position.set( position[0], position[1], position[2] );
						if ( rotation ) object.rotation.set( rotation[0], rotation[1], rotation[2] );
137

138
						parent.add( object );
A
Aleksandar Rodic 已提交
139

140
					}
A
Aleksandar Rodic 已提交
141

142
				}
A
Aleksandar Rodic 已提交
143

144
			};
A
Aleksandar Rodic 已提交
145

146 147
			setupGizmos(this.handleGizmos, this.handles);
			setupGizmos(this.pickerGizmos, this.pickers);
148

149
			// reset Transformations
A
Aleksandar Rodic 已提交
150

151
			this.traverse(function ( child ) {
M
Mr.doob 已提交
152 153 154
				if (child instanceof THREE.Mesh) {
					child.updateMatrix();

155
					var tempGeometry = new THREE.Geometry();
M
Mr.doob 已提交
156
					tempGeometry.merge( child.geometry, child.matrix );
157

158 159 160 161 162 163
					child.geometry = tempGeometry;
					child.position.set( 0, 0, 0 );
					child.rotation.set( 0, 0, 0 );
					child.scale.set( 1, 1, 1 );
				}
			});
A
Aleksandar Rodic 已提交
164

165
		};
A
Aleksandar Rodic 已提交
166

167 168 169 170 171
		this.hide = function () {
			this.traverse(function( child ) {
				child.visible = false;
			});
		};
A
Aleksandar Rodic 已提交
172

173 174 175 176 177 178 179 180
		this.show = function () {
			this.traverse(function( child ) {
				child.visible = true;
				if (child.parent == scope.pickers ) child.visible = showPickers;
				if (child.parent == scope.planes ) child.visible = false;
			});
			this.activePlane.visible = showActivePlane;
		};
181

182 183 184 185 186 187 188 189 190 191 192
		this.highlight = function ( axis ) {
			this.traverse(function( child ) {
				if ( child.material && child.material.highlight ){
					if ( child.name == axis ) {
						child.material.highlight( true );
					} else {
						child.material.highlight( false );
					}
				}
			});
		};
A
Aleksandar Rodic 已提交
193

194
	};
195

196
	THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype );
197

198
	THREE.TransformGizmo.prototype.update = function ( rotation, eye ) {
199

200 201 202
		var vec1 = new THREE.Vector3( 0, 0, 0 );
		var vec2 = new THREE.Vector3( 0, 1, 0 );
		var lookAtMatrix = new THREE.Matrix4();
203

204 205 206 207 208
		this.traverse(function(child) {
			if ( child.name.search("E") != -1 ) {
				child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) );
			} else if ( child.name.search("X") != -1 || child.name.search("Y") != -1 || child.name.search("Z") != -1 ) {
				child.quaternion.setFromEuler( rotation );
209
			}
210
		});
211

212
	};
A
Aleksandar Rodic 已提交
213

214
	THREE.TransformGizmoTranslate = function () {
A
Aleksandar Rodic 已提交
215

216
		THREE.TransformGizmo.call( this );
217

218 219 220
		var arrowGeometry = new THREE.Geometry();
		var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) );
		mesh.position.y = 0.5;
221 222 223
		mesh.updateMatrix();

		arrowGeometry.merge( mesh.geometry, mesh.matrix );
224

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
		var lineXGeometry = new THREE.Geometry();
		lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );

		var lineYGeometry = new THREE.Geometry();
		lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );

		var lineZGeometry = new THREE.Geometry();
		lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) );

		this.handleGizmos = {
			X: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI/2 ] ],
				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
			],
			Y: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
				[	new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
			],
			Z: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI/2, 0, 0 ] ],
				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
			],
			XYZ: [
				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
			],
			XY: [
				[ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ]
			],
			YZ: [
				[ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI/2, 0 ] ]
			],
			XZ: [
				[ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ -Math.PI/2, 0, 0 ] ]
			]
		};
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
		this.pickerGizmos = {
			X: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI/2 ] ]
			],
			Y: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ]
			],
			Z: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI/2, 0, 0 ] ]
			],
			XYZ: [
				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
			],
			XY: [
				[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.2, 0.2, 0 ] ]
			],
			YZ: [
				[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.2, 0.2 ], [ 0, Math.PI/2, 0 ] ]
			],
			XZ: [
				[ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.2, 0, 0.2 ], [ -Math.PI/2, 0, 0 ] ]
			]
		};
A
Aleksandar Rodic 已提交
284

285
		this.setActivePlane = function ( axis, eye ) {
286

287
			var tempMatrix = new THREE.Matrix4();
288
			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
289

290 291 292 293
			if ( axis == "X" ) {
				this.activePlane = this.planes[ "XY" ];
				if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ];
			}
294

295 296 297 298
			if ( axis == "Y" ){
				this.activePlane = this.planes[ "XY" ];
				if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ];
			}
299

300 301 302 303
			if ( axis == "Z" ){
				this.activePlane = this.planes[ "XZ" ];
				if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ];
			}
304

305
			if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
A
Aleksandar Rodic 已提交
306

307
			if ( axis == "XY" ) this.activePlane = this.planes[ "XY" ];
A
Aleksandar Rodic 已提交
308

309
			if ( axis == "YZ" ) this.activePlane = this.planes[ "YZ" ];
A
Aleksandar Rodic 已提交
310

311
			if ( axis == "XZ" ) this.activePlane = this.planes[ "XZ" ];
312

313 314
			this.hide();
			this.show();
A
Aleksandar Rodic 已提交
315

316
		};
A
Aleksandar Rodic 已提交
317

318
		this.init();
A
Aleksandar Rodic 已提交
319

320
	};
A
Aleksandar Rodic 已提交
321

322
	THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype );
A
Aleksandar Rodic 已提交
323

324
	THREE.TransformGizmoRotate = function () {
A
Aleksandar Rodic 已提交
325

326
		THREE.TransformGizmo.call( this );
327

328
		var CircleGeometry = function ( radius, facing, arc ) {
329

330 331 332 333 334 335 336
				var geometry = new THREE.Geometry();
				arc = arc ? arc : 1;
				for ( var i = 0; i <= 64 * arc; ++i ) {
					if ( facing == 'x' ) geometry.vertices.push( new THREE.Vector3( 0, Math.cos( i / 32 * Math.PI ), Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
					if ( facing == 'y' ) geometry.vertices.push( new THREE.Vector3( Math.cos( i / 32 * Math.PI ), 0, Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
					if ( facing == 'z' ) geometry.vertices.push( new THREE.Vector3( Math.sin( i / 32 * Math.PI ), Math.cos( i / 32 * Math.PI ), 0 ).multiplyScalar(radius) );
				}
337

338 339
				return geometry;
		};
A
Aleksandar Rodic 已提交
340

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
		this.handleGizmos = {
			X: [
				[ new THREE.Line( new CircleGeometry(1,'x',0.5), new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
			],
			Y: [
				[ new THREE.Line( new CircleGeometry(1,'y',0.5), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
			],
			Z: [
				[ new THREE.Line( new CircleGeometry(1,'z',0.5), new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
			],
			E: [
				[ new THREE.Line( new CircleGeometry(1.25,'z',1), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ]
			],
			XYZE: [
				[ new THREE.Line( new CircleGeometry(1,'z',1), new GizmoLineMaterial( { color: 0x787878 } ) ) ]
			]
		};
358

359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
		this.pickerGizmos = {
			X: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, -Math.PI/2, -Math.PI/2 ] ]
			],
			Y: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ Math.PI/2, 0, 0 ] ]
			],
			Z: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, -Math.PI/2 ] ]
			],
			E: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.12, 2, 24 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ) ]
			],
			XYZE: [
				[ new THREE.Mesh( new THREE.Geometry() ) ]// TODO
			]
		};
A
Aleksandar Rodic 已提交
376

377
		this.setActivePlane = function ( axis ) {
A
Aleksandar Rodic 已提交
378

379
			if ( axis == "E" ) this.activePlane = this.planes[ "XYZE" ];
380

381
			if ( axis == "X" ) this.activePlane = this.planes[ "YZ" ];
A
Aleksandar Rodic 已提交
382

383
			if ( axis == "Y" ) this.activePlane = this.planes[ "XZ" ];
A
Aleksandar Rodic 已提交
384

385
			if ( axis == "Z" ) this.activePlane = this.planes[ "XY" ];
A
Aleksandar Rodic 已提交
386

387 388
			this.hide();
			this.show();
A
Aleksandar Rodic 已提交
389

390
		};
A
Aleksandar Rodic 已提交
391

392
		this.update = function ( rotation, eye2 ) {
A
Aleksandar Rodic 已提交
393

394
			THREE.TransformGizmo.prototype.update.apply( this, arguments );
A
Aleksandar Rodic 已提交
395

396 397 398 399
			var group = {
				handles: this["handles"],
				pickers: this["pickers"],
			};
A
Aleksandar Rodic 已提交
400

401 402 403 404 405 406 407 408 409 410
			var tempMatrix = new THREE.Matrix4();
			var worldRotation = new THREE.Euler( 0, 0, 1 );
			var tempQuaternion = new THREE.Quaternion();
			var unitX = new THREE.Vector3( 1, 0, 0 );
			var unitY = new THREE.Vector3( 0, 1, 0 );
			var unitZ = new THREE.Vector3( 0, 0, 1 );
			var quaternionX = new THREE.Quaternion();
			var quaternionY = new THREE.Quaternion();
			var quaternionZ = new THREE.Quaternion();
			var eye = eye2.clone();
A
Aleksandar Rodic 已提交
411

412 413
			worldRotation.copy( this.planes["XY"].rotation );
			tempQuaternion.setFromEuler( worldRotation );
414

415
			tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
416
			eye.applyMatrix4( tempMatrix );
417

418
			this.traverse(function(child) {
A
Aleksandar Rodic 已提交
419

420
				tempQuaternion.setFromEuler( worldRotation );
A
Aleksandar Rodic 已提交
421

422
				if ( child.name == "X" ) {
423 424
					quaternionX.setFromAxisAngle( unitX, Math.atan2( -eye.y, eye.z ) );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
425
					child.quaternion.copy( tempQuaternion );
A
Aleksandar Rodic 已提交
426 427
				}

428
				if ( child.name == "Y" ) {
429 430
					quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
431
					child.quaternion.copy( tempQuaternion );
432 433
				}

434
				if ( child.name == "Z" ) {
435 436
					quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
437
					child.quaternion.copy( tempQuaternion );
438
				}
A
Aleksandar Rodic 已提交
439

440
			});
A
Aleksandar Rodic 已提交
441

442
		};
443

444
		this.init();
A
Aleksandar Rodic 已提交
445

446
	};
A
Aleksandar Rodic 已提交
447

448 449 450 451 452 453 454 455 456
	THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype );

	THREE.TransformGizmoScale = function () {

		THREE.TransformGizmo.call( this );

		var arrowGeometry = new THREE.Geometry();
		var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) );
		mesh.position.y = 0.5;
457 458 459
		mesh.updateMatrix();

		arrowGeometry.merge( mesh.geometry, mesh.matrix );
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486

		var lineXGeometry = new THREE.Geometry();
		lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );

		var lineYGeometry = new THREE.Geometry();
		lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );

		var lineZGeometry = new THREE.Geometry();
		lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) );

		this.handleGizmos = {
			X: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI/2 ] ],
				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
			],
			Y: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
				[ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
			],
			Z: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI/2, 0, 0 ] ],
				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
			],
			XYZ: [
				[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
			]
		};
A
Aleksandar Rodic 已提交
487

488 489 490 491 492 493 494 495 496 497 498 499 500 501
		this.pickerGizmos = {
			X: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI/2 ] ]
			],
			Y: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ]
			],
			Z: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI/2, 0, 0 ] ]
			],
			XYZ: [
				[ new THREE.Mesh( new THREE.BoxGeometry( 0.4, 0.4, 0.4 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
			]
		};
502

503
		this.setActivePlane = function ( axis, eye ) {
A
Aleksandar Rodic 已提交
504

505
			var tempMatrix = new THREE.Matrix4();
506
			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
A
Aleksandar Rodic 已提交
507

508 509 510 511
			if ( axis == "X" ) {
				this.activePlane = this.planes[ "XY" ];
				if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ];
			}
A
Aleksandar Rodic 已提交
512

513 514 515 516
			if ( axis == "Y" ){
				this.activePlane = this.planes[ "XY" ];
				if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ];
			}
A
Aleksandar Rodic 已提交
517

518 519 520 521
			if ( axis == "Z" ){
				this.activePlane = this.planes[ "XZ" ];
				if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ];
			}
522

523
			if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
524

525 526
			this.hide();
			this.show();
A
Aleksandar Rodic 已提交
527

528
		};
A
Aleksandar Rodic 已提交
529

530
		this.init();
531

532
	};
A
Aleksandar Rodic 已提交
533

534
	THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype );
A
Aleksandar Rodic 已提交
535

536
	THREE.TransformControls = function ( camera, domElement ) {
A
Aleksandar Rodic 已提交
537

538 539
		// TODO: Make non-uniform scale and rotate play nice in hierarchies
		// TODO: ADD RXYZ contol
A
Aleksandar Rodic 已提交
540

541
		THREE.Object3D.call( this );
A
Aleksandar Rodic 已提交
542

543
		domElement = ( domElement !== undefined ) ? domElement : document;
544

545 546 547 548
		this.gizmo = {};
		this.gizmo["translate"] = new THREE.TransformGizmoTranslate();
		this.gizmo["rotate"] = new THREE.TransformGizmoRotate();
		this.gizmo["scale"] = new THREE.TransformGizmoScale();
549

550 551 552
		this.add(this.gizmo["translate"]);
		this.add(this.gizmo["rotate"]);
		this.add(this.gizmo["scale"]);
553

554 555 556
		this.gizmo["translate"].hide();
		this.gizmo["rotate"].hide();
		this.gizmo["scale"].hide();
557

558
		this.object = undefined;
559
		this.snap = null;
560 561
		this.space = "world";
		this.size = 1;
562
		this.axis = null;
563

564
		var scope = this;
565

566 567 568
		var _dragging = false;
		var _mode = "translate";
		var _plane = "XY";
569

570
		var changeEvent = { type: "change" };
571 572
		var mouseDownEvent = { type: "mouseDown" };
		var mouseUpEvent = { type: "mouseUp", mode: _mode };
A
Aleksandar Rodic 已提交
573

574 575 576
		var ray = new THREE.Raycaster();
		var projector = new THREE.Projector();
		var pointerVector = new THREE.Vector3();
A
Aleksandar Rodic 已提交
577

578 579
		var point = new THREE.Vector3();
		var offset = new THREE.Vector3();
A
Aleksandar Rodic 已提交
580

581 582 583
		var rotation = new THREE.Vector3();
		var offsetRotation = new THREE.Vector3();
		var scale = 1;
584

585 586
		var lookAtMatrix = new THREE.Matrix4();
		var eye = new THREE.Vector3();
587

588 589 590 591 592 593
		var tempMatrix = new THREE.Matrix4();
		var tempVector = new THREE.Vector3();
		var tempQuaternion = new THREE.Quaternion();
		var unitX = new THREE.Vector3( 1, 0, 0 );
		var unitY = new THREE.Vector3( 0, 1, 0 );
		var unitZ = new THREE.Vector3( 0, 0, 1 );
594

595 596 597 598 599
		var quaternionXYZ = new THREE.Quaternion();
		var quaternionX = new THREE.Quaternion();
		var quaternionY = new THREE.Quaternion();
		var quaternionZ = new THREE.Quaternion();
		var quaternionE = new THREE.Quaternion();
600

601 602 603
		var oldPosition = new THREE.Vector3();
		var oldScale = new THREE.Vector3();
		var oldRotationMatrix = new THREE.Matrix4();
604

605 606
		var parentRotationMatrix  = new THREE.Matrix4();
		var parentScale = new THREE.Vector3();
607

608 609 610 611 612
		var worldPosition = new THREE.Vector3();
		var worldRotation = new THREE.Euler();
		var worldRotationMatrix  = new THREE.Matrix4();
		var camPosition = new THREE.Vector3();
		var camRotation = new THREE.Euler();
613

614 615
		domElement.addEventListener( "mousedown", onPointerDown, false );
		domElement.addEventListener( "touchstart", onPointerDown, false );
616

617 618
		domElement.addEventListener( "mousemove", onPointerHover, false );
		domElement.addEventListener( "touchmove", onPointerHover, false );
619

620 621
		domElement.addEventListener( "mousemove", onPointerMove, false );
		domElement.addEventListener( "touchmove", onPointerMove, false );
A
Aleksandar Rodic 已提交
622

623 624 625 626 627
		domElement.addEventListener( "mouseup", onPointerUp, false );
		domElement.addEventListener( "mouseout", onPointerUp, false );
		domElement.addEventListener( "touchend", onPointerUp, false );
		domElement.addEventListener( "touchcancel", onPointerUp, false );
		domElement.addEventListener( "touchleave", onPointerUp, false );
A
Aleksandar Rodic 已提交
628

629
		this.attach = function ( object ) {
A
Aleksandar Rodic 已提交
630

631
			scope.object = object;
A
Aleksandar Rodic 已提交
632

633 634 635 636
			this.gizmo["translate"].hide();
			this.gizmo["rotate"].hide();
			this.gizmo["scale"].hide();
			this.gizmo[_mode].show();
A
Aleksandar Rodic 已提交
637

638
			scope.update();
A
Aleksandar Rodic 已提交
639

640
		};
A
Aleksandar Rodic 已提交
641

642
		this.detach = function ( object ) {
A
Aleksandar Rodic 已提交
643

644
			scope.object = undefined;
645
			this.axis = null;
A
Aleksandar Rodic 已提交
646

647 648 649
			this.gizmo["translate"].hide();
			this.gizmo["rotate"].hide();
			this.gizmo["scale"].hide();
A
Aleksandar Rodic 已提交
650

651
		};
A
Aleksandar Rodic 已提交
652

653
		this.setMode = function ( mode ) {
A
Aleksandar Rodic 已提交
654

655
			_mode = mode ? mode : _mode;
A
Aleksandar Rodic 已提交
656

657
			if ( _mode == "scale" ) scope.space = "local";
A
Aleksandar Rodic 已提交
658

659 660
			this.gizmo["translate"].hide();
			this.gizmo["rotate"].hide();
661
			this.gizmo["scale"].hide();
662
			this.gizmo[_mode].show();
A
Aleksandar Rodic 已提交
663

664 665
			this.update();
			scope.dispatchEvent( changeEvent );
A
Aleksandar Rodic 已提交
666

667
		};
668

669
		this.setSnap = function ( snap ) {
670

671
			scope.snap = snap;
672

673
		};
A
Aleksandar Rodic 已提交
674

675
		this.setSize = function ( size ) {
676

677 678 679
			scope.size = size;
			this.update();
			scope.dispatchEvent( changeEvent );
680

681
		};
682

683
		this.setSpace = function ( space ) {
684

685 686 687
			scope.space = space;
			this.update();
			scope.dispatchEvent( changeEvent );
688

689
		};
690

691
		this.update = function () {
692

693
			if ( scope.object === undefined ) return;
694

695 696 697
			scope.object.updateMatrixWorld();
			worldPosition.setFromMatrixPosition( scope.object.matrixWorld );
			worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) );
698

699 700 701
			camera.updateMatrixWorld();
			camPosition.setFromMatrixPosition( camera.matrixWorld );
			camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );
702

703 704 705
			scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size;
			this.position.copy( worldPosition );
			this.scale.set( scale, scale, scale );
706

707
			eye.copy( camPosition ).sub( worldPosition ).normalize();
708

709 710
			if ( scope.space == "local" )
				this.gizmo[_mode].update( worldRotation, eye );
711

712 713
			else if ( scope.space == "world" )
				this.gizmo[_mode].update( new THREE.Euler(), eye );
714

715
			this.gizmo[_mode].highlight( scope.axis );
716

717
		};
718

719
		function onPointerHover( event ) {
720

721
			if ( scope.object === undefined || _dragging === true ) return;
722

723
			event.preventDefault();
724

725
			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
726 727 728

			var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );

729 730
			var axis = null;

731 732
			if ( intersect ) {

733
				axis = intersect.object.name;
734

735 736 737
			}

			if ( scope.axis !== axis ) {
738

739
				scope.axis = axis;
740 741 742 743
				scope.update();
				scope.dispatchEvent( changeEvent );

			}
744 745 746

		}

747
		function onPointerDown( event ) {
748

749
			if ( scope.object === undefined || _dragging === true ) return;
750

751 752
			event.preventDefault();
			event.stopPropagation();
A
Aleksandar Rodic 已提交
753

754
			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
755

756
			if ( pointer.button === 0 || pointer.button === undefined ) {
A
Aleksandar Rodic 已提交
757

758
				var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
A
Aleksandar Rodic 已提交
759

760
				if ( intersect ) {
A
Aleksandar Rodic 已提交
761

762
					scope.dispatchEvent( mouseDownEvent );
D
Daniel Taub 已提交
763

764
					scope.axis = intersect.object.name;
A
Aleksandar Rodic 已提交
765

766
					scope.update();
A
Aleksandar Rodic 已提交
767

768
					eye.copy( camPosition ).sub( worldPosition ).normalize();
A
Aleksandar Rodic 已提交
769

770
					scope.gizmo[_mode].setActivePlane( scope.axis, eye );
771

772
					var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] );
773

774 775
					oldPosition.copy( scope.object.position );
					oldScale.copy( scope.object.scale );
A
Aleksandar Rodic 已提交
776

777 778
					oldRotationMatrix.extractRotation( scope.object.matrix );
					worldRotationMatrix.extractRotation( scope.object.matrixWorld );
A
Aleksandar Rodic 已提交
779

780 781
					parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
					parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
A
Aleksandar Rodic 已提交
782

783
					offset.copy( planeIntersect.point );
A
Aleksandar Rodic 已提交
784

785
				}
A
Aleksandar Rodic 已提交
786 787 788

			}

789 790
			_dragging = true;

A
Aleksandar Rodic 已提交
791 792
		}

793
		function onPointerMove( event ) {
A
Aleksandar Rodic 已提交
794

795
			if ( scope.object === undefined || scope.axis === null || _dragging === false ) return;
A
Aleksandar Rodic 已提交
796

797 798
			event.preventDefault();
			event.stopPropagation();
A
Aleksandar Rodic 已提交
799

800
			var pointer = event.changedTouches? event.changedTouches[0] : event;
A
Aleksandar Rodic 已提交
801

802
			var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] );
A
Aleksandar Rodic 已提交
803

804
			point.copy( planeIntersect.point );
A
Aleksandar Rodic 已提交
805

806
			if ( _mode == "translate" ) {
A
Aleksandar Rodic 已提交
807

808 809
				point.sub( offset );
				point.multiply(parentScale);
A
Aleksandar Rodic 已提交
810

811
				if ( scope.space == "local" ) {
A
Aleksandar Rodic 已提交
812

813
					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
814

815 816 817
					if ( scope.axis.search("X") == -1 ) point.x = 0;
					if ( scope.axis.search("Y") == -1 ) point.y = 0;
					if ( scope.axis.search("Z") == -1 ) point.z = 0;
A
Aleksandar Rodic 已提交
818

819
					point.applyMatrix4( oldRotationMatrix );
A
Aleksandar Rodic 已提交
820

821 822
					scope.object.position.copy( oldPosition );
					scope.object.position.add( point );
A
Aleksandar Rodic 已提交
823

D
Daniel Taub 已提交
824
				}
A
Aleksandar Rodic 已提交
825

826
				if ( scope.space == "world" || scope.axis.search("XYZ") != -1 ) {
A
Aleksandar Rodic 已提交
827

828 829 830
					if ( scope.axis.search("X") == -1 ) point.x = 0;
					if ( scope.axis.search("Y") == -1 ) point.y = 0;
					if ( scope.axis.search("Z") == -1 ) point.z = 0;
A
Aleksandar Rodic 已提交
831

832
					point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
A
Aleksandar Rodic 已提交
833

834 835
					scope.object.position.copy( oldPosition );
					scope.object.position.add( point );
836

837
				}
838

839
				if ( scope.snap !== null ) {
840

841 842 843
					if ( scope.axis.search("X") != -1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.snap ) * scope.snap;
					if ( scope.axis.search("Y") != -1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.snap ) * scope.snap;
					if ( scope.axis.search("Z") != -1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.snap ) * scope.snap;
844

845
				}
846

847
			} else if ( _mode == "scale" ) {
A
Aleksandar Rodic 已提交
848

849 850
				point.sub( offset );
				point.multiply(parentScale);
A
Aleksandar Rodic 已提交
851

852
				if ( scope.space == "local" ) {
A
Aleksandar Rodic 已提交
853

854
					if ( scope.axis == "XYZ") {
855

856
						scale = 1 + ( ( point.y ) / 50 );
A
Aleksandar Rodic 已提交
857

858 859 860
						scope.object.scale.x = oldScale.x * scale;
						scope.object.scale.y = oldScale.y * scale;
						scope.object.scale.z = oldScale.z * scale;
A
Aleksandar Rodic 已提交
861

862
					} else {
863

864
						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
A
Aleksandar Rodic 已提交
865

866 867 868
						if ( scope.axis == "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 );
						if ( scope.axis == "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 );
						if ( scope.axis == "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 );
A
Aleksandar Rodic 已提交
869

870
					}
A
Aleksandar Rodic 已提交
871

872
				}
A
Aleksandar Rodic 已提交
873

874
			} else if ( _mode == "rotate" ) {
A
Aleksandar Rodic 已提交
875

876 877 878 879
				point.sub( worldPosition );
				point.multiply(parentScale);
				tempVector.copy(offset).sub( worldPosition );
				tempVector.multiply(parentScale);
A
Aleksandar Rodic 已提交
880

881
				if ( scope.axis == "E" ) {
A
Aleksandar Rodic 已提交
882

883 884
					point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
					tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
A
Aleksandar Rodic 已提交
885

886 887
					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
A
Aleksandar Rodic 已提交
888

889
					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
A
Aleksandar Rodic 已提交
890

891 892
					quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
893

894 895
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
A
Aleksandar Rodic 已提交
896

897
					scope.object.quaternion.copy( tempQuaternion );
898

899
				} else if ( scope.axis == "XYZE" ) {
A
Aleksandar Rodic 已提交
900

901
					quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis
902

903 904 905
					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
					quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
906

907 908
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
A
Aleksandar Rodic 已提交
909

910
					scope.object.quaternion.copy( tempQuaternion );
A
Aleksandar Rodic 已提交
911

912
				} else if ( scope.space == "local" ) {
A
Aleksandar Rodic 已提交
913

914
					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
A
Aleksandar Rodic 已提交
915

916
					tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
A
Aleksandar Rodic 已提交
917

918 919
					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
A
Aleksandar Rodic 已提交
920

921 922 923 924
					quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
925

926 927 928
					if ( scope.axis == "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
					if ( scope.axis == "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
					if ( scope.axis == "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
A
Aleksandar Rodic 已提交
929

930
					scope.object.quaternion.copy( quaternionXYZ );
A
Aleksandar Rodic 已提交
931

932
				} else if ( scope.space == "world" ) {
A
Aleksandar Rodic 已提交
933

934 935
					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
A
Aleksandar Rodic 已提交
936

937
					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
A
Aleksandar Rodic 已提交
938

939 940 941 942
					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
A
Aleksandar Rodic 已提交
943

944 945 946
					if ( scope.axis == "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					if ( scope.axis == "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
					if ( scope.axis == "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
947

948
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
A
Aleksandar Rodic 已提交
949

950
					scope.object.quaternion.copy( tempQuaternion );
A
Aleksandar Rodic 已提交
951

952
				}
A
Aleksandar Rodic 已提交
953 954 955

			}

956
			scope.update();
957
			scope.dispatchEvent( changeEvent );
A
Aleksandar Rodic 已提交
958

959
		}
A
Aleksandar Rodic 已提交
960

961
		function onPointerUp( event ) {
A
Aleksandar Rodic 已提交
962

D
Daniel Taub 已提交
963
			if ( _dragging && ( scope.axis !== null ) ) {
964 965
				mouseUpEvent.mode = _mode;
				scope.dispatchEvent( mouseUpEvent )
966
			}
967 968
			_dragging = false;
			onPointerHover( event );
A
Aleksandar Rodic 已提交
969

970
		}
A
Aleksandar Rodic 已提交
971

972
		function intersectObjects( pointer, objects ) {
A
Aleksandar Rodic 已提交
973

974 975 976 977
			var rect = domElement.getBoundingClientRect();
			var x = (pointer.clientX - rect.left) / rect.width;
			var y = (pointer.clientY - rect.top) / rect.height;
			pointerVector.set( ( x ) * 2 - 1, - ( y ) * 2 + 1, 0.5 );
A
Aleksandar Rodic 已提交
978

979 980
			projector.unprojectVector( pointerVector, camera );
			ray.set( camPosition, pointerVector.sub( camPosition ).normalize() );
A
Aleksandar Rodic 已提交
981

982 983
			var intersections = ray.intersectObjects( objects, true );
			return intersections[0] ? intersections[0] : false;
984

985
		}
986

987
	};
A
Aleksandar Rodic 已提交
988

989
	THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype );
A
Aleksandar Rodic 已提交
990

D
Daniel Taub 已提交
991
}());