Ray.tests.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /* global QUnit */
  2. import { Ray } from '../../../../src/math/Ray.js';
  3. import { Box3 } from '../../../../src/math/Box3.js';
  4. import { Vector3 } from '../../../../src/math/Vector3.js';
  5. import { Sphere } from '../../../../src/math/Sphere.js';
  6. import { Plane } from '../../../../src/math/Plane.js';
  7. import { Matrix4 } from '../../../../src/math/Matrix4.js';
  8. import {
  9. zero3,
  10. one3,
  11. two3,
  12. eps,
  13. posInf3
  14. } from '../../utils/math-constants.js';
  15. export default QUnit.module( 'Maths', () => {
  16. QUnit.module( 'Ray', () => {
  17. // INSTANCING
  18. QUnit.test( 'Instancing', ( assert ) => {
  19. let a = new Ray();
  20. assert.ok( a.origin.equals( zero3 ), 'Passed!' );
  21. assert.ok( a.direction.equals( new Vector3( 0, 0, - 1 ) ), 'Passed!' );
  22. a = new Ray( two3.clone(), one3.clone() );
  23. assert.ok( a.origin.equals( two3 ), 'Passed!' );
  24. assert.ok( a.direction.equals( one3 ), 'Passed!' );
  25. } );
  26. // PUBLIC
  27. QUnit.test( 'set', ( assert ) => {
  28. const a = new Ray();
  29. a.set( one3, one3 );
  30. assert.ok( a.origin.equals( one3 ), 'Passed!' );
  31. assert.ok( a.direction.equals( one3 ), 'Passed!' );
  32. } );
  33. QUnit.test( 'recast/clone', ( assert ) => {
  34. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  35. assert.ok( a.recast( 0 ).equals( a ), 'Passed!' );
  36. const b = a.clone();
  37. assert.ok( b.recast( - 1 ).equals( new Ray( new Vector3( 1, 1, 0 ), new Vector3( 0, 0, 1 ) ) ), 'Passed!' );
  38. const c = a.clone();
  39. assert.ok( c.recast( 1 ).equals( new Ray( new Vector3( 1, 1, 2 ), new Vector3( 0, 0, 1 ) ) ), 'Passed!' );
  40. const d = a.clone();
  41. const e = d.clone().recast( 1 );
  42. assert.ok( d.equals( a ), 'Passed!' );
  43. assert.ok( ! e.equals( d ), 'Passed!' );
  44. assert.ok( e.equals( c ), 'Passed!' );
  45. } );
  46. QUnit.test( 'copy/equals', ( assert ) => {
  47. const a = new Ray( zero3.clone(), one3.clone() );
  48. const b = new Ray().copy( a );
  49. assert.ok( b.origin.equals( zero3 ), 'Passed!' );
  50. assert.ok( b.direction.equals( one3 ), 'Passed!' );
  51. // ensure that it is a true copy
  52. a.origin = zero3;
  53. a.direction = one3;
  54. assert.ok( b.origin.equals( zero3 ), 'Passed!' );
  55. assert.ok( b.direction.equals( one3 ), 'Passed!' );
  56. } );
  57. QUnit.test( 'at', ( assert ) => {
  58. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  59. const point = new Vector3();
  60. a.at( 0, point );
  61. assert.ok( point.equals( one3 ), 'Passed!' );
  62. a.at( - 1, point );
  63. assert.ok( point.equals( new Vector3( 1, 1, 0 ) ), 'Passed!' );
  64. a.at( 1, point );
  65. assert.ok( point.equals( new Vector3( 1, 1, 2 ) ), 'Passed!' );
  66. } );
  67. QUnit.test( 'lookAt', ( assert ) => {
  68. const a = new Ray( two3.clone(), one3.clone() );
  69. const target = one3.clone();
  70. const expected = target.sub( two3 ).normalize();
  71. a.lookAt( target );
  72. assert.ok( a.direction.equals( expected ), 'Check if we\'re looking in the right direction' );
  73. } );
  74. QUnit.test( 'closestPointToPoint', ( assert ) => {
  75. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  76. const point = new Vector3();
  77. // behind the ray
  78. a.closestPointToPoint( zero3, point );
  79. assert.ok( point.equals( one3 ), 'Passed!' );
  80. // front of the ray
  81. a.closestPointToPoint( new Vector3( 0, 0, 50 ), point );
  82. assert.ok( point.equals( new Vector3( 1, 1, 50 ) ), 'Passed!' );
  83. // exactly on the ray
  84. a.closestPointToPoint( one3, point );
  85. assert.ok( point.equals( one3 ), 'Passed!' );
  86. } );
  87. QUnit.test( 'distanceToPoint', ( assert ) => {
  88. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  89. // behind the ray
  90. const b = a.distanceToPoint( zero3 );
  91. assert.ok( b === Math.sqrt( 3 ), 'Passed!' );
  92. // front of the ray
  93. const c = a.distanceToPoint( new Vector3( 0, 0, 50 ) );
  94. assert.ok( c === Math.sqrt( 2 ), 'Passed!' );
  95. // exactly on the ray
  96. const d = a.distanceToPoint( one3 );
  97. assert.ok( d === 0, 'Passed!' );
  98. } );
  99. QUnit.test( 'distanceSqToPoint', ( assert ) => {
  100. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  101. // behind the ray
  102. const b = a.distanceSqToPoint( zero3 );
  103. assert.ok( b === 3, 'Passed!' );
  104. // front of the ray
  105. const c = a.distanceSqToPoint( new Vector3( 0, 0, 50 ) );
  106. assert.ok( c === 2, 'Passed!' );
  107. // exactly on the ray
  108. const d = a.distanceSqToPoint( one3 );
  109. assert.ok( d === 0, 'Passed!' );
  110. } );
  111. QUnit.test( 'distanceSqToSegment', ( assert ) => {
  112. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  113. const ptOnLine = new Vector3();
  114. const ptOnSegment = new Vector3();
  115. //segment in front of the ray
  116. let v0 = new Vector3( 3, 5, 50 );
  117. let v1 = new Vector3( 50, 50, 50 ); // just a far away point
  118. let distSqr = a.distanceSqToSegment( v0, v1, ptOnLine, ptOnSegment );
  119. assert.ok( ptOnSegment.distanceTo( v0 ) < 0.0001, 'Passed!' );
  120. assert.ok( ptOnLine.distanceTo( new Vector3( 1, 1, 50 ) ) < 0.0001, 'Passed!' );
  121. // ((3-1) * (3-1) + (5-1) * (5-1) = 4 + 16 = 20
  122. assert.ok( Math.abs( distSqr - 20 ) < 0.0001, 'Passed!' );
  123. //segment behind the ray
  124. v0 = new Vector3( - 50, - 50, - 50 ); // just a far away point
  125. v1 = new Vector3( - 3, - 5, - 4 );
  126. distSqr = a.distanceSqToSegment( v0, v1, ptOnLine, ptOnSegment );
  127. assert.ok( ptOnSegment.distanceTo( v1 ) < 0.0001, 'Passed!' );
  128. assert.ok( ptOnLine.distanceTo( one3 ) < 0.0001, 'Passed!' );
  129. // ((-3-1) * (-3-1) + (-5-1) * (-5-1) + (-4-1) + (-4-1) = 16 + 36 + 25 = 77
  130. assert.ok( Math.abs( distSqr - 77 ) < 0.0001, 'Passed!' );
  131. //exact intersection between the ray and the segment
  132. v0 = new Vector3( - 50, - 50, - 50 );
  133. v1 = new Vector3( 50, 50, 50 );
  134. distSqr = a.distanceSqToSegment( v0, v1, ptOnLine, ptOnSegment );
  135. assert.ok( ptOnSegment.distanceTo( one3 ) < 0.0001, 'Passed!' );
  136. assert.ok( ptOnLine.distanceTo( one3 ) < 0.0001, 'Passed!' );
  137. assert.ok( distSqr < 0.0001, 'Passed!' );
  138. } );
  139. QUnit.test( 'intersectSphere', ( assert ) => {
  140. const TOL = 0.0001;
  141. const point = new Vector3();
  142. // ray a0 origin located at ( 0, 0, 0 ) and points outward in negative-z direction
  143. const a0 = new Ray( zero3.clone(), new Vector3( 0, 0, - 1 ) );
  144. // ray a1 origin located at ( 1, 1, 1 ) and points left in negative-x direction
  145. const a1 = new Ray( one3.clone(), new Vector3( - 1, 0, 0 ) );
  146. // sphere (radius of 2) located behind ray a0, should result in null
  147. let b = new Sphere( new Vector3( 0, 0, 3 ), 2 );
  148. a0.intersectSphere( b, point.copy( posInf3 ) );
  149. assert.ok( point.equals( posInf3 ), 'Passed!' );
  150. // sphere (radius of 2) located in front of, but too far right of ray a0, should result in null
  151. b = new Sphere( new Vector3( 3, 0, - 1 ), 2 );
  152. a0.intersectSphere( b, point.copy( posInf3 ) );
  153. assert.ok( point.equals( posInf3 ), 'Passed!' );
  154. // sphere (radius of 2) located below ray a1, should result in null
  155. b = new Sphere( new Vector3( 1, - 2, 1 ), 2 );
  156. a1.intersectSphere( b, point.copy( posInf3 ) );
  157. assert.ok( point.equals( posInf3 ), 'Passed!' );
  158. // sphere (radius of 1) located to the left of ray a1, should result in intersection at 0, 1, 1
  159. b = new Sphere( new Vector3( - 1, 1, 1 ), 1 );
  160. a1.intersectSphere( b, point );
  161. assert.ok( point.distanceTo( new Vector3( 0, 1, 1 ) ) < TOL, 'Passed!' );
  162. // sphere (radius of 1) located in front of ray a0, should result in intersection at 0, 0, -1
  163. b = new Sphere( new Vector3( 0, 0, - 2 ), 1 );
  164. a0.intersectSphere( b, point );
  165. assert.ok( point.distanceTo( new Vector3( 0, 0, - 1 ) ) < TOL, 'Passed!' );
  166. // sphere (radius of 2) located in front & right of ray a0, should result in intersection at 0, 0, -1, or left-most edge of sphere
  167. b = new Sphere( new Vector3( 2, 0, - 1 ), 2 );
  168. a0.intersectSphere( b, point );
  169. assert.ok( point.distanceTo( new Vector3( 0, 0, - 1 ) ) < TOL, 'Passed!' );
  170. // same situation as above, but move the sphere a fraction more to the right, and ray a0 should now just miss
  171. b = new Sphere( new Vector3( 2.01, 0, - 1 ), 2 );
  172. a0.intersectSphere( b, point.copy( posInf3 ) );
  173. assert.ok( point.equals( posInf3 ), 'Passed!' );
  174. // following QUnit.tests are for situations where the ray origin is inside the sphere
  175. // sphere (radius of 1) center located at ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 1,
  176. // is behind ray a0. Therefore, second exit point on back of sphere will be returned: 0, 0, -1
  177. // thus keeping the intersection point always in front of the ray.
  178. b = new Sphere( zero3.clone(), 1 );
  179. a0.intersectSphere( b, point );
  180. assert.ok( point.distanceTo( new Vector3( 0, 0, - 1 ) ) < TOL, 'Passed!' );
  181. // sphere (radius of 4) center located behind ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 5,
  182. // is behind ray a0. Therefore, second exit point on back of sphere will be returned: 0, 0, -3
  183. // thus keeping the intersection point always in front of the ray.
  184. b = new Sphere( new Vector3( 0, 0, 1 ), 4 );
  185. a0.intersectSphere( b, point );
  186. assert.ok( point.distanceTo( new Vector3( 0, 0, - 3 ) ) < TOL, 'Passed!' );
  187. // sphere (radius of 4) center located in front of ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 3,
  188. // is behind ray a0. Therefore, second exit point on back of sphere will be returned: 0, 0, -5
  189. // thus keeping the intersection point always in front of the ray.
  190. b = new Sphere( new Vector3( 0, 0, - 1 ), 4 );
  191. a0.intersectSphere( b, point );
  192. assert.ok( point.distanceTo( new Vector3( 0, 0, - 5 ) ) < TOL, 'Passed!' );
  193. } );
  194. QUnit.test( 'intersectsSphere', ( assert ) => {
  195. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  196. const b = new Sphere( zero3, 0.5 );
  197. const c = new Sphere( zero3, 1.5 );
  198. const d = new Sphere( one3, 0.1 );
  199. const e = new Sphere( two3, 0.1 );
  200. const f = new Sphere( two3, 1 );
  201. assert.ok( ! a.intersectsSphere( b ), 'Passed!' );
  202. assert.ok( ! a.intersectsSphere( c ), 'Passed!' );
  203. assert.ok( a.intersectsSphere( d ), 'Passed!' );
  204. assert.ok( ! a.intersectsSphere( e ), 'Passed!' );
  205. assert.ok( ! a.intersectsSphere( f ), 'Passed!' );
  206. } );
  207. QUnit.todo( 'distanceToPlane', ( assert ) => {
  208. assert.ok( false, 'everything\'s gonna be alright' );
  209. } );
  210. QUnit.test( 'intersectPlane', ( assert ) => {
  211. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  212. const point = new Vector3();
  213. // parallel plane behind
  214. const b = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 0, 0, 1 ), new Vector3( 1, 1, - 1 ) );
  215. a.intersectPlane( b, point.copy( posInf3 ) );
  216. assert.ok( point.equals( posInf3 ), 'Passed!' );
  217. // parallel plane coincident with origin
  218. const c = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 0, 0, 1 ), new Vector3( 1, 1, 0 ) );
  219. a.intersectPlane( c, point.copy( posInf3 ) );
  220. assert.ok( point.equals( posInf3 ), 'Passed!' );
  221. // parallel plane in front
  222. const d = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 0, 0, 1 ), new Vector3( 1, 1, 1 ) );
  223. a.intersectPlane( d, point.copy( posInf3 ) );
  224. assert.ok( point.equals( a.origin ), 'Passed!' );
  225. // perpendicular ray that overlaps exactly
  226. const e = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 1, 0, 0 ), one3 );
  227. a.intersectPlane( e, point.copy( posInf3 ) );
  228. assert.ok( point.equals( a.origin ), 'Passed!' );
  229. // perpendicular ray that doesn't overlap
  230. const f = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 1, 0, 0 ), zero3 );
  231. a.intersectPlane( f, point.copy( posInf3 ) );
  232. assert.ok( point.equals( posInf3 ), 'Passed!' );
  233. } );
  234. QUnit.test( 'intersectsPlane', ( assert ) => {
  235. const a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  236. // parallel plane in front of the ray
  237. const b = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 0, 0, 1 ), one3.clone().sub( new Vector3( 0, 0, - 1 ) ) );
  238. assert.ok( a.intersectsPlane( b ), 'Passed!' );
  239. // parallel plane coincident with origin
  240. const c = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 0, 0, 1 ), one3.clone().sub( new Vector3( 0, 0, 0 ) ) );
  241. assert.ok( a.intersectsPlane( c ), 'Passed!' );
  242. // parallel plane behind the ray
  243. const d = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 0, 0, 1 ), one3.clone().sub( new Vector3( 0, 0, 1 ) ) );
  244. assert.ok( ! a.intersectsPlane( d ), 'Passed!' );
  245. // perpendicular ray that overlaps exactly
  246. const e = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 1, 0, 0 ), one3 );
  247. assert.ok( a.intersectsPlane( e ), 'Passed!' );
  248. // perpendicular ray that doesn't overlap
  249. const f = new Plane().setFromNormalAndCoplanarPoint( new Vector3( 1, 0, 0 ), zero3 );
  250. assert.ok( ! a.intersectsPlane( f ), 'Passed!' );
  251. } );
  252. QUnit.test( 'intersectBox', ( assert ) => {
  253. const TOL = 0.0001;
  254. const box = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) );
  255. const point = new Vector3();
  256. const a = new Ray( new Vector3( - 2, 0, 0 ), new Vector3( 1, 0, 0 ) );
  257. //ray should intersect box at -1,0,0
  258. assert.ok( a.intersectsBox( box ) === true, 'Passed!' );
  259. a.intersectBox( box, point );
  260. assert.ok( point.distanceTo( new Vector3( - 1, 0, 0 ) ) < TOL, 'Passed!' );
  261. const b = new Ray( new Vector3( - 2, 0, 0 ), new Vector3( - 1, 0, 0 ) );
  262. //ray is point away from box, it should not intersect
  263. assert.ok( b.intersectsBox( box ) === false, 'Passed!' );
  264. b.intersectBox( box, point.copy( posInf3 ) );
  265. assert.ok( point.equals( posInf3 ), 'Passed!' );
  266. const c = new Ray( new Vector3( 0, 0, 0 ), new Vector3( 1, 0, 0 ) );
  267. // ray is inside box, should return exit point
  268. assert.ok( c.intersectsBox( box ) === true, 'Passed!' );
  269. c.intersectBox( box, point );
  270. assert.ok( point.distanceTo( new Vector3( 1, 0, 0 ) ) < TOL, 'Passed!' );
  271. const d = new Ray( new Vector3( 0, 2, 1 ), new Vector3( 0, - 1, - 1 ).normalize() );
  272. //tilted ray should intersect box at 0,1,0
  273. assert.ok( d.intersectsBox( box ) === true, 'Passed!' );
  274. d.intersectBox( box, point );
  275. assert.ok( point.distanceTo( new Vector3( 0, 1, 0 ) ) < TOL, 'Passed!' );
  276. const e = new Ray( new Vector3( 1, - 2, 1 ), new Vector3( 0, 1, 0 ).normalize() );
  277. //handle case where ray is coplanar with one of the boxes side - box in front of ray
  278. assert.ok( e.intersectsBox( box ) === true, 'Passed!' );
  279. e.intersectBox( box, point );
  280. assert.ok( point.distanceTo( new Vector3( 1, - 1, 1 ) ) < TOL, 'Passed!' );
  281. const f = new Ray( new Vector3( 1, - 2, 0 ), new Vector3( 0, - 1, 0 ).normalize() );
  282. //handle case where ray is coplanar with one of the boxes side - box behind ray
  283. assert.ok( f.intersectsBox( box ) === false, 'Passed!' );
  284. f.intersectBox( box, point.copy( posInf3 ) );
  285. assert.ok( point.equals( posInf3 ), 'Passed!' );
  286. } );
  287. QUnit.todo( 'intersectsBox', ( assert ) => {
  288. assert.ok( false, 'everything\'s gonna be alright' );
  289. } );
  290. QUnit.test( 'intersectTriangle', ( assert ) => {
  291. const ray = new Ray();
  292. const a = new Vector3( 1, 1, 0 );
  293. const b = new Vector3( 0, 1, 1 );
  294. const c = new Vector3( 1, 0, 1 );
  295. const point = new Vector3();
  296. // DdN == 0
  297. ray.set( ray.origin, zero3.clone() );
  298. ray.intersectTriangle( a, b, c, false, point.copy( posInf3 ) );
  299. assert.ok( point.equals( posInf3 ), 'No intersection if direction == zero' );
  300. // DdN > 0, backfaceCulling = true
  301. ray.set( ray.origin, one3.clone() );
  302. ray.intersectTriangle( a, b, c, true, point.copy( posInf3 ) );
  303. assert.ok( point.equals( posInf3 ), 'No intersection with backside faces if backfaceCulling is true' );
  304. // DdN > 0
  305. ray.set( ray.origin, one3.clone() );
  306. ray.intersectTriangle( a, b, c, false, point );
  307. assert.ok( Math.abs( point.x - 2 / 3 ) <= eps, 'Successful intersection: check x' );
  308. assert.ok( Math.abs( point.y - 2 / 3 ) <= eps, 'Successful intersection: check y' );
  309. assert.ok( Math.abs( point.z - 2 / 3 ) <= eps, 'Successful intersection: check z' );
  310. // DdN > 0, DdQxE2 < 0
  311. b.multiplyScalar( - 1 );
  312. ray.intersectTriangle( a, b, c, false, point.copy( posInf3 ) );
  313. assert.ok( point.equals( posInf3 ), 'No intersection' );
  314. // DdN > 0, DdE1xQ < 0
  315. a.multiplyScalar( - 1 );
  316. ray.intersectTriangle( a, b, c, false, point.copy( posInf3 ) );
  317. assert.ok( point.equals( posInf3 ), 'No intersection' );
  318. // DdN > 0, DdQxE2 + DdE1xQ > DdN
  319. b.multiplyScalar( - 1 );
  320. ray.intersectTriangle( a, b, c, false, point.copy( posInf3 ) );
  321. assert.ok( point.equals( posInf3 ), 'No intersection' );
  322. // DdN < 0, QdN < 0
  323. a.multiplyScalar( - 1 );
  324. b.multiplyScalar( - 1 );
  325. ray.direction.multiplyScalar( - 1 );
  326. ray.intersectTriangle( a, b, c, false, point.copy( posInf3 ) );
  327. assert.ok( point.equals( posInf3 ), 'No intersection when looking in the wrong direction' );
  328. } );
  329. QUnit.test( 'applyMatrix4', ( assert ) => {
  330. let a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );
  331. const m = new Matrix4();
  332. assert.ok( a.clone().applyMatrix4( m ).equals( a ), 'Passed!' );
  333. a = new Ray( zero3.clone(), new Vector3( 0, 0, 1 ) );
  334. m.makeRotationZ( Math.PI );
  335. assert.ok( a.clone().applyMatrix4( m ).equals( a ), 'Passed!' );
  336. m.makeRotationX( Math.PI );
  337. const b = a.clone();
  338. b.direction.negate();
  339. let a2 = a.clone().applyMatrix4( m );
  340. assert.ok( a2.origin.distanceTo( b.origin ) < 0.0001, 'Passed!' );
  341. assert.ok( a2.direction.distanceTo( b.direction ) < 0.0001, 'Passed!' );
  342. a.origin = new Vector3( 0, 0, 1 );
  343. b.origin = new Vector3( 0, 0, - 1 );
  344. a2 = a.clone().applyMatrix4( m );
  345. assert.ok( a2.origin.distanceTo( b.origin ) < 0.0001, 'Passed!' );
  346. assert.ok( a2.direction.distanceTo( b.direction ) < 0.0001, 'Passed!' );
  347. } );
  348. QUnit.todo( 'equals', ( assert ) => {
  349. assert.ok( false, 'everything\'s gonna be alright' );
  350. } );
  351. } );
  352. } );
粤ICP备19079148号