Object3D.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /**
  2. * @author simonThiele / https://github.com/simonThiele
  3. */
  4. QUnit.module( "Object3D" );
  5. var RadToDeg = 180 / Math.PI;
  6. var eulerEquals = function ( a, b, tolerance ) {
  7. tolerance = tolerance || 0.0001;
  8. if ( a.order != b.order ) {
  9. return false;
  10. }
  11. return (
  12. Math.abs( a.x - b.x ) <= tolerance &&
  13. Math.abs( a.y - b.y ) <= tolerance &&
  14. Math.abs( a.z - b.z ) <= tolerance
  15. );
  16. };
  17. QUnit.test( "rotateX" , function( assert ) {
  18. var obj = new THREE.Object3D();
  19. var angleInRad = 1.562;
  20. obj.rotateX(angleInRad);
  21. assert.numEqual( obj.rotation.x, angleInRad, "x is equal" );
  22. });
  23. QUnit.test( "rotateY" , function( assert ) {
  24. var obj = new THREE.Object3D();
  25. var angleInRad = -0.346;
  26. obj.rotateY(angleInRad);
  27. assert.numEqual( obj.rotation.y, angleInRad, "y is equal" );
  28. });
  29. QUnit.test( "rotateZ" , function( assert ) {
  30. var obj = new THREE.Object3D();
  31. var angleInRad = 1;
  32. obj.rotateZ(angleInRad);
  33. assert.numEqual( obj.rotation.z, angleInRad, "z is equal" );
  34. });
  35. QUnit.test( "translateOnAxis" , function( assert ) {
  36. var obj = new THREE.Object3D();
  37. obj.translateOnAxis(new THREE.Vector3(1, 0, 0), 1);
  38. obj.translateOnAxis(new THREE.Vector3(0, 1, 0), 1.23);
  39. obj.translateOnAxis(new THREE.Vector3(0, 0, 1), -4.56);
  40. assert.propEqual( obj.position, { x: 1, y: 1.23, z: -4.56 } );
  41. });
  42. QUnit.test( "translateX" , function( assert ) {
  43. var obj = new THREE.Object3D();
  44. obj.translateX(1.234);
  45. assert.numEqual( obj.position.x, 1.234, "x is equal" );
  46. });
  47. QUnit.test( "translateY" , function( assert ) {
  48. var obj = new THREE.Object3D();
  49. obj.translateY(1.234);
  50. assert.numEqual( obj.position.y, 1.234, "y is equal" );
  51. });
  52. QUnit.test( "translateZ" , function( assert ) {
  53. var obj = new THREE.Object3D();
  54. obj.translateZ(1.234);
  55. assert.numEqual( obj.position.z, 1.234, "z is equal" );
  56. });
  57. QUnit.test( "lookAt" , function( assert ) {
  58. var obj = new THREE.Object3D();
  59. obj.lookAt(new THREE.Vector3(0, -1, 1));
  60. assert.numEqual( obj.rotation.x * RadToDeg, 45, "x is equal" );
  61. });
  62. QUnit.test( "getWorldRotation" , function( assert ) {
  63. var obj = new THREE.Object3D();
  64. obj.lookAt(new THREE.Vector3(0, -1, 1));
  65. assert.numEqual( obj.getWorldRotation().x * RadToDeg, 45, "x is equal" );
  66. obj.lookAt(new THREE.Vector3(1, 0, 0));
  67. assert.numEqual( obj.getWorldRotation().y * RadToDeg, 90, "y is equal" );
  68. });
  69. QUnit.test( "getObjectById/getObjectByName/getObjectByProperty", function ( assert ) {
  70. var parent = new THREE.Object3D();
  71. var childName = new THREE.Object3D();
  72. var childId = new THREE.Object3D(); // id = parent.id + 2
  73. var childNothing = new THREE.Object3D();
  74. parent.prop = true;
  75. childName.name = "foo";
  76. parent.add( childName, childId, childNothing );
  77. assert.strictEqual( parent.getObjectByProperty( 'prop', true ), parent, "Get parent by its own property" );
  78. assert.strictEqual( parent.getObjectByName( "foo" ), childName, "Get child by name" );
  79. assert.strictEqual( parent.getObjectById( parent.id + 2 ), childId, "Get child by Id" );
  80. assert.strictEqual(
  81. parent.getObjectByProperty( 'no-property', 'no-value' ), undefined,
  82. "Unknown property results in undefined"
  83. );
  84. } );
  85. QUnit.test( "setRotationFromAxisAngle", function ( assert ) {
  86. var a = new THREE.Object3D();
  87. var axis = new THREE.Vector3( 0, 1, 0 );
  88. var angle = Math.PI;
  89. var expected = new THREE.Euler( - Math.PI, 0, - Math.PI );
  90. a.setRotationFromAxisAngle( axis, angle );
  91. assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after rotation" );
  92. axis.set( 1, 0, 0 );
  93. angle = 0;
  94. expected.set( 0, 0, 0 );
  95. a.setRotationFromAxisAngle( axis, angle );
  96. assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after zeroing" );
  97. } );
  98. QUnit.test( "setRotationFromEuler", function ( assert ) {
  99. var a = new THREE.Object3D();
  100. var rotation = new THREE.Euler( THREE.Math.degToRad( 45 ), 0, Math.PI );
  101. var expected = rotation.clone(); // bit obvious
  102. a.setRotationFromEuler( rotation );
  103. assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after rotation" );
  104. } );
  105. QUnit.test( "setRotationFromQuaternion", function ( assert ) {
  106. var a = new THREE.Object3D();
  107. var rotation = new THREE.Quaternion().setFromEuler( new THREE.Euler( Math.PI, 0, - Math.PI ) );
  108. var expected = new THREE.Euler( Math.PI, 0, - Math.PI );
  109. a.setRotationFromQuaternion( rotation );
  110. assert.ok( eulerEquals( a.getWorldRotation(), expected ), "Correct values after rotation" );
  111. } );
  112. QUnit.test( "setRotationFromMatrix", function ( assert ) {
  113. var a = new THREE.Object3D();
  114. var m = new THREE.Matrix4();
  115. var eye = new THREE.Vector3( 0, 0, 0 );
  116. var target = new THREE.Vector3( 0, 1, - 1 );
  117. var up = new THREE.Vector3( 0, 1, 0 );
  118. m.lookAt( eye, target, up );
  119. a.setRotationFromMatrix( m );
  120. assert.numEqual( a.getWorldRotation().x * RadToDeg, 45, "Correct rotation angle" );
  121. } );
  122. QUnit.test( "copy", function ( assert ) {
  123. var a = new THREE.Object3D();
  124. var b = new THREE.Object3D();
  125. var child = new THREE.Object3D();
  126. var childChild = new THREE.Object3D();
  127. a.name = "original";
  128. b.name = "to-be-copied";
  129. b.position.set( x, y, z );
  130. b.quaternion.set( x, y, z, w );
  131. b.scale.set( 2, 3, 4 );
  132. // bogus test values
  133. b.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
  134. b.matrixWorld.set( 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 );
  135. b.matrixAutoUpdate = false;
  136. b.matrixWorldNeedsUpdate = true;
  137. b.layers.mask = 2;
  138. b.visible = false;
  139. b.castShadow = true;
  140. b.receiveShadow = true;
  141. b.frustumCulled = false;
  142. b.renderOrder = 1;
  143. b.userData[ "foo" ] = "bar";
  144. child.add( childChild );
  145. b.add( child );
  146. assert.notDeepEqual( a, b, "Objects are not equal pre-copy()" );
  147. a.copy( b, true );
  148. // check they're all unique instances
  149. assert.ok(
  150. a.uuid !== b.uuid &&
  151. a.children[ 0 ].uuid !== b.children[ 0 ].uuid &&
  152. a.children[ 0 ].children[ 0 ].uuid !== b.children[ 0 ].children[ 0 ].uuid,
  153. "UUIDs are all different"
  154. );
  155. // and now fix that
  156. a.uuid = b.uuid;
  157. a.children[ 0 ].uuid = b.children[ 0 ].uuid;
  158. a.children[ 0 ].children[ 0 ].uuid = b.children[ 0 ].children[ 0 ].uuid;
  159. assert.deepEqual( a, b, "Objects are equal post-copy()" );
  160. } );
  161. QUnit.test( "clone", function ( assert ) {
  162. var a;
  163. var b = new THREE.Object3D();
  164. assert.strictEqual( a, undefined, "Undefined pre-clone()" );
  165. a = b.clone();
  166. assert.notStrictEqual( a, b, "Defined but seperate instances post-clone()" );
  167. a.uuid = b.uuid;
  168. assert.deepEqual( a, b, "But identical properties" );
  169. } );
  170. QUnit.test( "toJSON", function ( assert ) {
  171. var a = new THREE.Object3D();
  172. var child = new THREE.Object3D();
  173. var childChild = new THREE.Object3D();
  174. a.name = "a's name";
  175. a.matrix.set( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 );
  176. a.visible = false;
  177. a.castShadow = true;
  178. a.receiveShadow = true;
  179. a.userData[ "foo" ] = "bar";
  180. child.uuid = "5D4E9AE8-DA61-4912-A575-71A5BE3D72CD";
  181. childChild.uuid = "B43854B3-E970-4E85-BD41-AAF8D7BFA189";
  182. child.add( childChild );
  183. a.add( child );
  184. var gold = {
  185. "metadata": {
  186. "version": 4.5,
  187. "type": "Object",
  188. "generator": "Object3D.toJSON"
  189. },
  190. "object": {
  191. "uuid": "0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2",
  192. "type": "Object3D",
  193. "name": "a's name",
  194. "castShadow": true,
  195. "receiveShadow": true,
  196. "visible": false,
  197. "userData": { "foo": "bar" },
  198. "matrix": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  199. "children": [
  200. {
  201. "uuid": "5D4E9AE8-DA61-4912-A575-71A5BE3D72CD",
  202. "type": "Object3D",
  203. "matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
  204. "children": [
  205. {
  206. "uuid": "B43854B3-E970-4E85-BD41-AAF8D7BFA189",
  207. "type": "Object3D",
  208. "matrix": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
  209. }
  210. ]
  211. }
  212. ]
  213. }
  214. };
  215. // hacks
  216. var out = a.toJSON();
  217. out.object.uuid = "0A1E4F43-CB5B-4097-8F82-DC2969C0B8C2";
  218. assert.deepEqual( out, gold, "JSON is as expected" );
  219. } );
  220. QUnit.test( "add/remove", function ( assert ) {
  221. var a = new THREE.Object3D();
  222. var child1 = new THREE.Object3D();
  223. var child2 = new THREE.Object3D();
  224. assert.strictEqual( a.children.length, 0, "Starts with no children" );
  225. a.add( child1 );
  226. assert.strictEqual( a.children.length, 1, "The first child was added" );
  227. assert.strictEqual( a.children[ 0 ], child1, "It's the right one" );
  228. a.add( child2 );
  229. assert.strictEqual( a.children.length, 2, "The second child was added" );
  230. assert.strictEqual( a.children[ 1 ], child2, "It's the right one" );
  231. assert.strictEqual( a.children[ 0 ], child1, "The first one is still there" );
  232. a.remove( child1 );
  233. assert.strictEqual( a.children.length, 1, "The first child was removed" );
  234. assert.strictEqual( a.children[ 0 ], child2, "The second one is still there" );
  235. a.add( child1 );
  236. a.remove( child1, child2 );
  237. assert.strictEqual( a.children.length, 0, "Both children were removed at once" );
  238. child1.add( child2 );
  239. assert.strictEqual( child1.children.length, 1, "The second child was added to the first one" );
  240. a.add( child2 );
  241. assert.strictEqual( a.children.length, 1, "The second one was added to the parent (no remove)" );
  242. assert.strictEqual( a.children[ 0 ], child2, "The second one is now the parent's child again" );
  243. assert.strictEqual( child1.children.length, 0, "The first one no longer has any children" );
  244. } );
  245. QUnit.test( "applyQuaternion", function ( assert ) {
  246. var a = new THREE.Object3D();
  247. var sqrt = 0.5 * Math.sqrt( 2 );
  248. var quat = new THREE.Quaternion( 0, sqrt, 0, sqrt );
  249. var expected = new THREE.Quaternion( sqrt / 2, sqrt / 2, 0, 0 );
  250. a.quaternion.set( 0.25, 0.25, 0.25, 0.25 );
  251. a.applyQuaternion( quat );
  252. assert.ok(
  253. Math.abs( a.quaternion.x - expected.x ) <= eps &&
  254. Math.abs( a.quaternion.y - expected.y ) <= eps &&
  255. Math.abs( a.quaternion.z - expected.z ) <= eps,
  256. "Quaternion has the expected values"
  257. );
  258. } );
  259. QUnit.test( "applyMatrix", function ( assert ) {
  260. var a = new THREE.Object3D();
  261. var m = new THREE.Matrix4();
  262. var expectedPos = new THREE.Vector3( x, y, z );
  263. var expectedQuat = new THREE.Quaternion( 0.5 * Math.sqrt( 2 ), 0, 0, 0.5 * Math.sqrt( 2 ) );
  264. m.makeRotationX( Math.PI / 2 );
  265. m.setPosition( new THREE.Vector3( x, y, z ) );
  266. a.applyMatrix( m );
  267. assert.deepEqual( a.position, expectedPos, "Position has the expected values" );
  268. assert.ok(
  269. Math.abs( a.quaternion.x - expectedQuat.x ) <= eps &&
  270. Math.abs( a.quaternion.y - expectedQuat.y ) <= eps &&
  271. Math.abs( a.quaternion.z - expectedQuat.z ) <= eps,
  272. "Quaternion has the expected values"
  273. );
  274. } );
  275. QUnit.test( "getWorldPosition", function ( assert ) {
  276. var a = new THREE.Object3D();
  277. var b = new THREE.Object3D();
  278. var expectedSingle = new THREE.Vector3( x, y, z );
  279. var expectedParent = new THREE.Vector3( x, y, 0 );
  280. var expectedChild = new THREE.Vector3( x, y, 7 + ( z - z ) );
  281. a.translateX( x );
  282. a.translateY( y );
  283. a.translateZ( z );
  284. assert.deepEqual(
  285. a.getWorldPosition(), expectedSingle,
  286. "WorldPosition as expected for single object"
  287. );
  288. // translate child and then parent
  289. b.translateZ( 7 );
  290. a.add( b );
  291. a.translateZ( - z );
  292. assert.deepEqual( a.getWorldPosition(), expectedParent, "WorldPosition as expected for parent" );
  293. assert.deepEqual( b.getWorldPosition(), expectedChild, "WorldPosition as expected for child" );
  294. } );
  295. QUnit.test( "getWorldScale", function ( assert ) {
  296. var a = new THREE.Object3D();
  297. var m = new THREE.Matrix4().makeScale( x, y, z );
  298. var expected = new THREE.Vector3( x, y, z );
  299. a.applyMatrix( m );
  300. assert.deepEqual( a.getWorldScale(), expected, "WorldScale as expected" );
  301. } );
  302. QUnit.test( "getWorldDirection", function ( assert ) {
  303. var a = new THREE.Object3D();
  304. var expected = new THREE.Vector3( 0, - 0.5 * Math.sqrt( 2 ), 0.5 * Math.sqrt( 2 ) );
  305. var dir;
  306. a.lookAt( new THREE.Vector3( 0, - 1, 1 ) );
  307. dir = a.getWorldDirection();
  308. assert.ok(
  309. Math.abs( dir.x - expected.x ) <= eps &&
  310. Math.abs( dir.y - expected.y ) <= eps &&
  311. Math.abs( dir.z - expected.z ) <= eps,
  312. "Direction has the expected values"
  313. );
  314. } );
  315. QUnit.test( "traverse/traverseVisible/traverseAncestors", function ( assert ) {
  316. var a = new THREE.Object3D();
  317. var b = new THREE.Object3D();
  318. var c = new THREE.Object3D();
  319. var d = new THREE.Object3D();
  320. var names = [];
  321. var expectedNormal = [ "parent", "child", "childchild 1", "childchild 2" ];
  322. var expectedVisible = [ "parent", "child", "childchild 2" ];
  323. var expectedAncestors = [ "child", "parent" ];
  324. a.name = "parent";
  325. b.name = "child";
  326. c.name = "childchild 1";
  327. c.visible = false;
  328. d.name = "childchild 2";
  329. b.add( c );
  330. b.add( d );
  331. a.add( b );
  332. a.traverse( function ( obj ) {
  333. names.push( obj.name );
  334. } );
  335. assert.deepEqual( names, expectedNormal, "Traversed objects in expected order" );
  336. names = [];
  337. a.traverseVisible( function ( obj ) {
  338. names.push( obj.name );
  339. } );
  340. assert.deepEqual( names, expectedVisible, "Traversed visible objects in expected order" );
  341. names = [];
  342. c.traverseAncestors( function ( obj ) {
  343. names.push( obj.name );
  344. } );
  345. assert.deepEqual( names, expectedAncestors, "Traversed ancestors in expected order" );
  346. } );