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 };
573
		var objectChangeEvent = { type: "objectChange" };
A
Aleksandar Rodic 已提交
574

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

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

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

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

589 590 591 592 593 594
		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 );
595

596 597 598 599 600
		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();
601

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

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

609 610 611 612 613
		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();
614

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

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

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

624 625 626 627 628
		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 已提交
629

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

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

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

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

641
		};
A
Aleksandar Rodic 已提交
642

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

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

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

652
		};
A
Aleksandar Rodic 已提交
653

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

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

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

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

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

668
		};
669

670
		this.setSnap = function ( snap ) {
671

672
			scope.snap = snap;
673

674
		};
A
Aleksandar Rodic 已提交
675

676
		this.setSize = function ( size ) {
677

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

682
		};
683

684
		this.setSpace = function ( space ) {
685

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

690
		};
691

692
		this.update = function () {
693

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

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

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

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

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

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

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

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

718
		};
719

720
		function onPointerHover( event ) {
721

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

724
			event.preventDefault();
725

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

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

730 731
			var axis = null;

732 733
			if ( intersect ) {

734
				axis = intersect.object.name;
735

736 737 738
			}

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

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

			}
745 746 747

		}

748
		function onPointerDown( event ) {
749

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

786
				}
A
Aleksandar Rodic 已提交
787 788 789

			}

790 791
			_dragging = true;

A
Aleksandar Rodic 已提交
792 793
		}

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

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

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

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

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

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

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

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

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

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

816 817 818
					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 已提交
819

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

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

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

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

829 830 831
					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 已提交
832

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

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

838
				}
839

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

842 843 844
					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;
845

846
				}
847

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

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

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

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

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

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

863
					} else {
864

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

867 868 869
						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 已提交
870

871
					}
A
Aleksandar Rodic 已提交
872

873
				}
A
Aleksandar Rodic 已提交
874

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

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

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

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

887 888
					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 已提交
889

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

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

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

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

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

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

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

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

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

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

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

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

919 920
					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 已提交
921

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

927 928 929
					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 已提交
930

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

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

935 936
					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 已提交
937

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

940 941 942 943
					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 已提交
944

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

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

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

953
				}
A
Aleksandar Rodic 已提交
954 955 956

			}

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

961
		}
A
Aleksandar Rodic 已提交
962

963
		function onPointerUp( event ) {
A
Aleksandar Rodic 已提交
964

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

972
		}
A
Aleksandar Rodic 已提交
973

974
		function intersectObjects( pointer, objects ) {
A
Aleksandar Rodic 已提交
975

976 977 978 979
			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 已提交
980

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

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

987
		}
988

989
	};
A
Aleksandar Rodic 已提交
990

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

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