TransformControls.js 29.7 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 128 129 130 131
					for ( i = gizmoMap[name].length; i--;) {
						
						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 137 138
						if ( position ) object.position.set( position[0], position[1], position[2] );
						if ( rotation ) object.rotation.set( rotation[0], rotation[1], rotation[2] );
						
						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 565 566 567 568
		var scope = this;
		
		var _dragging = false;
		var _mode = "translate";
		var _plane = "XY";
569

570
		var changeEvent = { type: "change" };
A
Aleksandar Rodic 已提交
571

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

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

579 580 581
		var rotation = new THREE.Vector3();
		var offsetRotation = new THREE.Vector3();
		var scale = 1;
582

583 584
		var lookAtMatrix = new THREE.Matrix4();
		var eye = new THREE.Vector3();
585

586 587 588 589 590 591
		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 );
592

593 594 595 596 597
		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();
598

599 600 601
		var oldPosition = new THREE.Vector3();
		var oldScale = new THREE.Vector3();
		var oldRotationMatrix = new THREE.Matrix4();
602

603 604
		var parentRotationMatrix  = new THREE.Matrix4();
		var parentScale = new THREE.Vector3();
605

606 607 608 609 610
		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();
611

612 613
		domElement.addEventListener( "mousedown", onPointerDown, false );
		domElement.addEventListener( "touchstart", onPointerDown, false );
614

615 616
		domElement.addEventListener( "mousemove", onPointerHover, false );
		domElement.addEventListener( "touchmove", onPointerHover, false );
617

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

621 622 623 624 625
		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 已提交
626

627
		this.attach = function ( object ) {
A
Aleksandar Rodic 已提交
628

629
			scope.object = object;
A
Aleksandar Rodic 已提交
630

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

636
			scope.update();
A
Aleksandar Rodic 已提交
637

638
		};
A
Aleksandar Rodic 已提交
639

640
		this.detach = function ( object ) {
A
Aleksandar Rodic 已提交
641

642 643
			scope.object = undefined;
			this.axis = undefined;
A
Aleksandar Rodic 已提交
644

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

649
		};
A
Aleksandar Rodic 已提交
650

651
		this.setMode = function ( mode ) {
A
Aleksandar Rodic 已提交
652

653
			_mode = mode ? mode : _mode;
A
Aleksandar Rodic 已提交
654

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

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

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

665
		};
666

667
		this.setSnap = function ( snap ) {
668

669
			scope.snap = snap;
670

671
		};
A
Aleksandar Rodic 已提交
672

673
		this.setSize = function ( size ) {
674

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

681
		this.setSpace = function ( space ) {
682

683 684 685
			scope.space = space;
			this.update();
			scope.dispatchEvent( changeEvent );
686

687
		};
688

689
		this.update = function () {
690

691
			if ( scope.object === undefined ) return;
692

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

697 698 699
			camera.updateMatrixWorld();
			camPosition.setFromMatrixPosition( camera.matrixWorld );
			camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );
700

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

705
			eye.copy( camPosition ).sub( worldPosition ).normalize();
706

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

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

713
			this.gizmo[_mode].highlight( scope.axis );
714

715
		};
716

717
		function onPointerHover( event ) {
718

719
			if ( scope.object === undefined || _dragging === true ) return;
720

721
			event.preventDefault();
722

723
			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
724 725 726 727 728 729 730 731 732

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

			if ( intersect ) {

				scope.axis = intersect.object.name;
				scope.update();
				scope.dispatchEvent( changeEvent );

733
			} else if ( scope.axis !== null ) {
734

735
				scope.axis = null;
736 737 738 739
				scope.update();
				scope.dispatchEvent( changeEvent );

			}
740 741 742

		}

743
		function onPointerDown( event ) {
744

745
			if ( scope.object === undefined || _dragging === true ) return;
746

747 748
			event.preventDefault();
			event.stopPropagation();
A
Aleksandar Rodic 已提交
749

750
			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
751

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

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

756
				if ( intersect ) {
A
Aleksandar Rodic 已提交
757

758
					scope.axis = intersect.object.name;
A
Aleksandar Rodic 已提交
759

760
					scope.update();
A
Aleksandar Rodic 已提交
761

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

764
					scope.gizmo[_mode].setActivePlane( scope.axis, eye );
765

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

768 769
					oldPosition.copy( scope.object.position );
					oldScale.copy( scope.object.scale );
A
Aleksandar Rodic 已提交
770

771 772
					oldRotationMatrix.extractRotation( scope.object.matrix );
					worldRotationMatrix.extractRotation( scope.object.matrixWorld );
A
Aleksandar Rodic 已提交
773

774 775
					parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
					parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
A
Aleksandar Rodic 已提交
776

777
					offset.copy( planeIntersect.point );
A
Aleksandar Rodic 已提交
778

779
				}
A
Aleksandar Rodic 已提交
780 781 782

			}

783 784
			_dragging = true;

A
Aleksandar Rodic 已提交
785 786
		}

787
		function onPointerMove( event ) {
A
Aleksandar Rodic 已提交
788

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

791 792
			event.preventDefault();
			event.stopPropagation();
A
Aleksandar Rodic 已提交
793

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

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

798
			point.copy( planeIntersect.point );
A
Aleksandar Rodic 已提交
799

800
			if ( _mode == "translate" ) {
A
Aleksandar Rodic 已提交
801

802 803
				point.sub( offset );
				point.multiply(parentScale);
A
Aleksandar Rodic 已提交
804

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

807
					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
808

809 810 811
					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 已提交
812

813
					point.applyMatrix4( oldRotationMatrix );
A
Aleksandar Rodic 已提交
814

815 816
					scope.object.position.copy( oldPosition );
					scope.object.position.add( point );
A
Aleksandar Rodic 已提交
817

818
				} 
A
Aleksandar Rodic 已提交
819

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

822 823 824
					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 已提交
825

826
					point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
A
Aleksandar Rodic 已提交
827

828 829
					scope.object.position.copy( oldPosition );
					scope.object.position.add( point );
830

831 832
				}
				
833
				if ( scope.snap !== null ) {
834 835 836 837 838 839
				
					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;
				
				}
840

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

843 844
				point.sub( offset );
				point.multiply(parentScale);
A
Aleksandar Rodic 已提交
845

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

848
					if ( scope.axis == "XYZ") {
849

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

852 853 854
						scope.object.scale.x = oldScale.x * scale;
						scope.object.scale.y = oldScale.y * scale;
						scope.object.scale.z = oldScale.z * scale;
A
Aleksandar Rodic 已提交
855

856
					} else {
857

858
						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
A
Aleksandar Rodic 已提交
859

860 861 862
						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 已提交
863

864
					}
A
Aleksandar Rodic 已提交
865

866
				}
A
Aleksandar Rodic 已提交
867

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

870 871 872 873
				point.sub( worldPosition );
				point.multiply(parentScale);
				tempVector.copy(offset).sub( worldPosition );
				tempVector.multiply(parentScale);
A
Aleksandar Rodic 已提交
874

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

877 878
					point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
					tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
A
Aleksandar Rodic 已提交
879

880 881
					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 已提交
882

883
					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
A
Aleksandar Rodic 已提交
884

885 886
					quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
887

888 889
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
A
Aleksandar Rodic 已提交
890

891
					scope.object.quaternion.copy( tempQuaternion );
892

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

895
					quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis
896

897 898 899
					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
					quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
900

901 902
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
A
Aleksandar Rodic 已提交
903

904
					scope.object.quaternion.copy( tempQuaternion );
A
Aleksandar Rodic 已提交
905

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

908
					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
A
Aleksandar Rodic 已提交
909

910
					tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
A
Aleksandar Rodic 已提交
911

912 913
					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 已提交
914

915 916 917 918
					quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
919

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

924
					scope.object.quaternion.copy( quaternionXYZ );
A
Aleksandar Rodic 已提交
925

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

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

931
					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
A
Aleksandar Rodic 已提交
932

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

938 939 940
					if ( scope.axis == "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					if ( scope.axis == "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
					if ( scope.axis == "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
941

942
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
A
Aleksandar Rodic 已提交
943

944
					scope.object.quaternion.copy( tempQuaternion );
A
Aleksandar Rodic 已提交
945

946
				}
A
Aleksandar Rodic 已提交
947 948 949

			}

950 951
			scope.update();
			scope.dispatchEvent( changeEvent );
A
Aleksandar Rodic 已提交
952

953
		}
A
Aleksandar Rodic 已提交
954

955
		function onPointerUp( event ) {
A
Aleksandar Rodic 已提交
956

957 958
			_dragging = false;
			onPointerHover( event );
A
Aleksandar Rodic 已提交
959

960
		}
A
Aleksandar Rodic 已提交
961

962
		function intersectObjects( pointer, objects ) {
A
Aleksandar Rodic 已提交
963

964 965 966 967
			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 已提交
968

969 970
			projector.unprojectVector( pointerVector, camera );
			ray.set( camPosition, pointerVector.sub( camPosition ).normalize() );
A
Aleksandar Rodic 已提交
971

972 973
			var intersections = ray.intersectObjects( objects, true );
			return intersections[0] ? intersections[0] : false;
974

975
		}
976

977
	};
A
Aleksandar Rodic 已提交
978

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

981
}());