rect_test.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.math.RectTest');
  15. goog.setTestOnly('goog.math.RectTest');
  16. goog.require('goog.math.Box');
  17. goog.require('goog.math.Coordinate');
  18. goog.require('goog.math.Rect');
  19. goog.require('goog.math.Size');
  20. goog.require('goog.testing.jsunit');
  21. /**
  22. * Produce legible assertion results. If two rects are not equal, the error
  23. * message will be of the form
  24. * "Expected <(1, 2 - 10 x 10)> (Object) but was <(3, 4 - 20 x 20)> (Object)"
  25. */
  26. function assertRectsEqual(expected, actual) {
  27. if (!goog.math.Rect.equals(expected, actual)) {
  28. assertEquals(expected, actual);
  29. }
  30. }
  31. /**
  32. * Create a goog.math.Rect with given coordinates.
  33. *
  34. * @param {Array<number>} a Array of numbers with given coordinates for rect,
  35. * with expected order [x1, y1, x2, y2].
  36. * @return {?goog.math.Rect} A rectangle, if coordinates were given.
  37. */
  38. function createRect(a) {
  39. return a ? new goog.math.Rect(a[0], a[1], a[2] - a[0], a[3] - a[1]) : null;
  40. }
  41. /**
  42. * Create a rect like object.
  43. *
  44. * @param {Array<number>} a Array of numbers with given coordinates for rect,
  45. * with expected order [x1, y1, x2, y2].
  46. * @return {?{left: number, top: number, width: number, height: number}} A rect
  47. * like object.
  48. */
  49. function createIRect(a) {
  50. if (a) {
  51. return {left: a[0], top: a[1], width: a[2] - a[0], height: a[3] - a[1]};
  52. }
  53. return null;
  54. }
  55. function testRectClone() {
  56. var r = new goog.math.Rect(0, 0, 0, 0);
  57. assertRectsEqual(r, r.clone());
  58. r.left = -10;
  59. r.top = -20;
  60. r.width = 10;
  61. r.height = 20;
  62. assertRectsEqual(r, r.clone());
  63. }
  64. function testRectIntersection() {
  65. var tests = [
  66. [[10, 10, 20, 20], [15, 15, 25, 25], [15, 15, 20, 20]],
  67. [[10, 10, 20, 20], [20, 0, 30, 10], [20, 10, 20, 10]],
  68. [[0, 0, 1, 1], [10, 11, 12, 13], null],
  69. [[11, 12, 98, 99], [22, 23, 34, 35], [22, 23, 34, 35]]
  70. ];
  71. var intersectTest = function(r0, r1, expected) {
  72. assertRectsEqual(expected, goog.math.Rect.intersection(r0, r1));
  73. assertRectsEqual(expected, goog.math.Rect.intersection(r1, r0));
  74. // Test in place methods.
  75. var clone = r0.clone();
  76. assertRectsEqual(expected, clone.intersection(r1) ? clone : null);
  77. if (r1.intersection) {
  78. assertRectsEqual(expected, r1.intersection(r0) ? r1 : null);
  79. }
  80. };
  81. for (var i = 0; i < tests.length; ++i) {
  82. var t = tests[i];
  83. var r0 = createRect(t[0]);
  84. var r1 = createRect(t[1]);
  85. var expected = createRect(t[2]);
  86. intersectTest(r0, r1, expected);
  87. }
  88. // Run same tests with IRects.
  89. for (var i = 0; i < tests.length; ++i) {
  90. var t = tests[i];
  91. var r0 = createRect(t[0]);
  92. var r1 = createIRect(t[1]);
  93. var expected = createRect(t[2]);
  94. intersectTest(r0, r1, expected);
  95. }
  96. }
  97. function testRectIntersects() {
  98. var r0 = createRect([10, 10, 20, 20]);
  99. var r1 = createRect([15, 15, 25, 25]);
  100. var r2 = createRect([0, 0, 1, 1]);
  101. var ri0 = createIRect([10, 10, 20, 20]);
  102. var ri1 = createIRect([15, 15, 25, 25]);
  103. var ri2 = createIRect([0, 0, 1, 1]);
  104. assertTrue(goog.math.Rect.intersects(r0, r1));
  105. assertTrue(goog.math.Rect.intersects(r1, r0));
  106. assertTrue(r0.intersects(r1));
  107. assertTrue(r1.intersects(r0));
  108. assertTrue(goog.math.Rect.intersects(r0, ri1));
  109. assertTrue(goog.math.Rect.intersects(ri0, ri1));
  110. assertTrue(goog.math.Rect.intersects(ri1, r0));
  111. assertTrue(r0.intersects(ri1));
  112. assertFalse(goog.math.Rect.intersects(r0, r2));
  113. assertFalse(goog.math.Rect.intersects(r2, r0));
  114. assertFalse(r0.intersects(r2));
  115. assertFalse(r2.intersects(r0));
  116. assertFalse(goog.math.Rect.intersects(ri0, ri2));
  117. assertFalse(goog.math.Rect.intersects(ri2, ri0));
  118. assertFalse(r0.intersects(ri2));
  119. assertFalse(r2.intersects(ri0));
  120. }
  121. function testRectBoundingRect() {
  122. var tests = [
  123. [[10, 10, 20, 20], [15, 15, 25, 25], [10, 10, 25, 25]],
  124. [[10, 10, 20, 20], [20, 0, 30, 10], [10, 0, 30, 20]],
  125. [[0, 0, 1, 1], [10, 11, 12, 13], [0, 0, 12, 13]],
  126. [[11, 12, 98, 99], [22, 23, 34, 35], [11, 12, 98, 99]]
  127. ];
  128. for (var i = 0; i < tests.length; ++i) {
  129. var t = tests[i];
  130. var r0 = createRect(t[0]);
  131. var r1 = createRect(t[1]);
  132. var expected = createRect(t[2]);
  133. assertRectsEqual(expected, goog.math.Rect.boundingRect(r0, r1));
  134. assertRectsEqual(expected, goog.math.Rect.boundingRect(r1, r0));
  135. // Test in place methods.
  136. var clone = r0.clone();
  137. clone.boundingRect(r1);
  138. assertRectsEqual(expected, clone);
  139. r1.boundingRect(r0);
  140. assertRectsEqual(expected, r1);
  141. }
  142. }
  143. function testRectDifference() {
  144. // B is the same as A.
  145. assertDifference([10, 10, 20, 20], [10, 10, 20, 20], []);
  146. // B does not touch A.
  147. assertDifference([10, 10, 20, 20], [0, 0, 5, 5], [[10, 10, 20, 20]]);
  148. // B overlaps top half of A.
  149. assertDifference([10, 10, 20, 20], [5, 15, 25, 25], [[10, 10, 20, 15]]);
  150. // B overlaps bottom half of A.
  151. assertDifference([10, 10, 20, 20], [5, 5, 25, 15], [[10, 15, 20, 20]]);
  152. // B overlaps right half of A.
  153. assertDifference([10, 10, 20, 20], [15, 5, 25, 25], [[10, 10, 15, 20]]);
  154. // B overlaps left half of A.
  155. assertDifference([10, 10, 20, 20], [5, 5, 15, 25], [[15, 10, 20, 20]]);
  156. // B touches A at its bottom right corner
  157. assertDifference([10, 10, 20, 20], [20, 20, 30, 30], [[10, 10, 20, 20]]);
  158. // B touches A at its top left corner
  159. assertDifference([10, 10, 20, 20], [5, 5, 10, 10], [[10, 10, 20, 20]]);
  160. // B touches A along its bottom edge
  161. assertDifference([10, 10, 20, 20], [12, 20, 17, 25], [[10, 10, 20, 20]]);
  162. // B splits A horizontally.
  163. assertDifference(
  164. [10, 10, 20, 20], [5, 12, 25, 18], [[10, 10, 20, 12], [10, 18, 20, 20]]);
  165. // B splits A vertically.
  166. assertDifference(
  167. [10, 10, 20, 20], [12, 5, 18, 25], [[10, 10, 12, 20], [18, 10, 20, 20]]);
  168. // B subtracts a notch from the top of A.
  169. assertDifference(
  170. [10, 10, 20, 20], [12, 5, 18, 15],
  171. [[10, 15, 20, 20], [10, 10, 12, 15], [18, 10, 20, 15]]);
  172. // B subtracts a notch from the bottom left of A
  173. assertDifference([1, 6, 3, 9], [1, 7, 2, 9], [[1, 6, 3, 7], [2, 7, 3, 9]]);
  174. // B subtracts a notch from the bottom right of A
  175. assertDifference([1, 6, 3, 9], [2, 7, 3, 9], [[1, 6, 3, 7], [1, 7, 2, 9]]);
  176. // B subtracts a notch from the top left of A
  177. assertDifference([1, 6, 3, 9], [1, 6, 2, 8], [[1, 8, 3, 9], [2, 6, 3, 8]]);
  178. // B subtracts a notch from the top left of A (no coinciding edge)
  179. assertDifference([1, 6, 3, 9], [0, 5, 2, 8], [[1, 8, 3, 9], [2, 6, 3, 8]]);
  180. // B subtracts a hole from the center of A.
  181. assertDifference([-20, -20, -10, -10], [-18, -18, -12, -12], [
  182. [-20, -20, -10, -18], [-20, -12, -10, -10], [-20, -18, -18, -12],
  183. [-12, -18, -10, -12]
  184. ]);
  185. }
  186. function assertDifference(a, b, expected) {
  187. var r0 = createRect(a);
  188. var r1 = createRect(b);
  189. var diff = goog.math.Rect.difference(r0, r1);
  190. assertEquals(
  191. 'Wrong number of rectangles in difference ', expected.length,
  192. diff.length);
  193. for (var j = 0; j < expected.length; ++j) {
  194. var e = createRect(expected[j]);
  195. if (!goog.math.Rect.equals(e, diff[j])) {
  196. alert(j + ': ' + e + ' != ' + diff[j]);
  197. }
  198. assertRectsEqual(e, diff[j]);
  199. }
  200. // Test in place version
  201. var diff = r0.difference(r1);
  202. assertEquals(
  203. 'Wrong number of rectangles in in-place difference ', expected.length,
  204. diff.length);
  205. for (var j = 0; j < expected.length; ++j) {
  206. var e = createRect(expected[j]);
  207. if (!goog.math.Rect.equals(e, diff[j])) {
  208. alert(j + ': ' + e + ' != ' + diff[j]);
  209. }
  210. assertRectsEqual(e, diff[j]);
  211. }
  212. }
  213. function testRectToBox() {
  214. var r = new goog.math.Rect(0, 0, 0, 0);
  215. assertObjectEquals(new goog.math.Box(0, 0, 0, 0), r.toBox());
  216. r.top = 10;
  217. r.left = 10;
  218. r.width = 20;
  219. r.height = 20;
  220. assertObjectEquals(new goog.math.Box(10, 30, 30, 10), r.toBox());
  221. r.top = -10;
  222. r.left = 0;
  223. r.width = 10;
  224. r.height = 10;
  225. assertObjectEquals(new goog.math.Box(-10, 10, 0, 0), r.toBox());
  226. }
  227. function testBoxToRect() {
  228. var box = new goog.math.Box(0, 0, 0, 0);
  229. assertObjectEquals(
  230. new goog.math.Rect(0, 0, 0, 0), goog.math.Rect.createFromBox(box));
  231. box.top = 10;
  232. box.left = 15;
  233. box.right = 23;
  234. box.bottom = 27;
  235. assertObjectEquals(
  236. new goog.math.Rect(15, 10, 8, 17), goog.math.Rect.createFromBox(box));
  237. box.top = -10;
  238. box.left = 3;
  239. box.right = 12;
  240. box.bottom = 7;
  241. assertObjectEquals(
  242. new goog.math.Rect(3, -10, 9, 17), goog.math.Rect.createFromBox(box));
  243. }
  244. function testBoxToRectAndBack() {
  245. rectToBoxAndBackTest(new goog.math.Rect(8, 11, 20, 23));
  246. rectToBoxAndBackTest(new goog.math.Rect(9, 13, NaN, NaN));
  247. rectToBoxAndBackTest(new goog.math.Rect(10, 13, NaN, 21));
  248. rectToBoxAndBackTest(new goog.math.Rect(5, 7, 14, NaN));
  249. }
  250. function rectToBoxAndBackTest(rect) {
  251. var box = rect.toBox();
  252. var rect2 = goog.math.Rect.createFromBox(box);
  253. // Use toString for this test since otherwise NaN != NaN.
  254. assertObjectEquals(rect.toString(), rect2.toString());
  255. }
  256. function testRectToBoxAndBack() {
  257. // This doesn't work if left or top is undefined.
  258. boxToRectAndBackTest(new goog.math.Box(11, 13, 20, 17));
  259. boxToRectAndBackTest(new goog.math.Box(10, NaN, NaN, 11));
  260. boxToRectAndBackTest(new goog.math.Box(9, 14, NaN, 11));
  261. boxToRectAndBackTest(new goog.math.Box(10, NaN, 22, 15));
  262. }
  263. function boxToRectAndBackTest(box) {
  264. var rect = goog.math.Rect.createFromBox(box);
  265. var box2 = rect.toBox();
  266. // Use toString for this test since otherwise NaN != NaN.
  267. assertEquals(box.toString(), box2.toString());
  268. }
  269. function testRectContainsRect() {
  270. var r = new goog.math.Rect(-10, 0, 20, 10);
  271. assertTrue(r.contains(r));
  272. assertFalse(r.contains(new goog.math.Rect(NaN, NaN, NaN, NaN)));
  273. var r2 = new goog.math.Rect(0, 2, 5, 5);
  274. assertTrue(r.contains(r2));
  275. assertFalse(r2.contains(r));
  276. r2.left = -11;
  277. assertFalse(r.contains(r2));
  278. r2.left = 0;
  279. r2.width = 15;
  280. assertFalse(r.contains(r2));
  281. r2.width = 5;
  282. r2.height = 10;
  283. assertFalse(r.contains(r2));
  284. r2.top = 0;
  285. assertTrue(r.contains(r2));
  286. }
  287. function testRectContainsCoordinate() {
  288. var r = new goog.math.Rect(20, 40, 60, 80);
  289. // Test middle.
  290. assertTrue(r.contains(new goog.math.Coordinate(50, 80)));
  291. // Test edges.
  292. assertTrue(r.contains(new goog.math.Coordinate(20, 40)));
  293. assertTrue(r.contains(new goog.math.Coordinate(50, 40)));
  294. assertTrue(r.contains(new goog.math.Coordinate(80, 40)));
  295. assertTrue(r.contains(new goog.math.Coordinate(80, 80)));
  296. assertTrue(r.contains(new goog.math.Coordinate(80, 120)));
  297. assertTrue(r.contains(new goog.math.Coordinate(50, 120)));
  298. assertTrue(r.contains(new goog.math.Coordinate(20, 120)));
  299. assertTrue(r.contains(new goog.math.Coordinate(20, 80)));
  300. // Test outside.
  301. assertFalse(r.contains(new goog.math.Coordinate(0, 0)));
  302. assertFalse(r.contains(new goog.math.Coordinate(50, 0)));
  303. assertFalse(r.contains(new goog.math.Coordinate(100, 0)));
  304. assertFalse(r.contains(new goog.math.Coordinate(100, 80)));
  305. assertFalse(r.contains(new goog.math.Coordinate(100, 160)));
  306. assertFalse(r.contains(new goog.math.Coordinate(50, 160)));
  307. assertFalse(r.contains(new goog.math.Coordinate(0, 160)));
  308. assertFalse(r.contains(new goog.math.Coordinate(0, 80)));
  309. }
  310. function testGetSize() {
  311. assertObjectEquals(
  312. new goog.math.Size(60, 80), new goog.math.Rect(20, 40, 60, 80).getSize());
  313. }
  314. function testGetBottomRight() {
  315. assertObjectEquals(
  316. new goog.math.Coordinate(40, 60),
  317. new goog.math.Rect(10, 20, 30, 40).getBottomRight());
  318. }
  319. function testGetCenter() {
  320. assertObjectEquals(
  321. new goog.math.Coordinate(25, 40),
  322. new goog.math.Rect(10, 20, 30, 40).getCenter());
  323. }
  324. function testGetTopLeft() {
  325. assertObjectEquals(
  326. new goog.math.Coordinate(10, 20),
  327. new goog.math.Rect(10, 20, 30, 40).getTopLeft());
  328. }
  329. function testRectCeil() {
  330. var rect = new goog.math.Rect(11.4, 26.6, 17.8, 9.2);
  331. assertEquals(
  332. 'The function should return the target instance', rect, rect.ceil());
  333. assertRectsEqual(new goog.math.Rect(12, 27, 18, 10), rect);
  334. }
  335. function testRectFloor() {
  336. var rect = new goog.math.Rect(11.4, 26.6, 17.8, 9.2);
  337. assertEquals(
  338. 'The function should return the target instance', rect, rect.floor());
  339. assertRectsEqual(new goog.math.Rect(11, 26, 17, 9), rect);
  340. }
  341. function testRectRound() {
  342. var rect = new goog.math.Rect(11.4, 26.6, 17.8, 9.2);
  343. assertEquals(
  344. 'The function should return the target instance', rect, rect.round());
  345. assertRectsEqual(new goog.math.Rect(11, 27, 18, 9), rect);
  346. }
  347. function testRectTranslateCoordinate() {
  348. var rect = new goog.math.Rect(10, 40, 30, 20);
  349. var c = new goog.math.Coordinate(10, 5);
  350. assertEquals(
  351. 'The function should return the target instance', rect,
  352. rect.translate(c));
  353. assertRectsEqual(new goog.math.Rect(20, 45, 30, 20), rect);
  354. }
  355. function testRectTranslateXY() {
  356. var rect = new goog.math.Rect(10, 20, 40, 35);
  357. assertEquals(
  358. 'The function should return the target instance', rect,
  359. rect.translate(15, 10));
  360. assertRectsEqual(new goog.math.Rect(25, 30, 40, 35), rect);
  361. }
  362. function testRectTranslateX() {
  363. var rect = new goog.math.Rect(12, 34, 113, 88);
  364. assertEquals(
  365. 'The function should return the target instance', rect,
  366. rect.translate(10));
  367. assertRectsEqual(new goog.math.Rect(22, 34, 113, 88), rect);
  368. }
  369. function testRectScaleXY() {
  370. var rect = new goog.math.Rect(10, 30, 100, 60);
  371. assertEquals(
  372. 'The function should return the target instance', rect, rect.scale(2, 5));
  373. assertRectsEqual(new goog.math.Rect(20, 150, 200, 300), rect);
  374. }
  375. function testRectScaleFactor() {
  376. var rect = new goog.math.Rect(12, 34, 113, 88);
  377. assertEquals(
  378. 'The function should return the target instance', rect, rect.scale(10));
  379. assertRectsEqual(new goog.math.Rect(120, 340, 1130, 880), rect);
  380. }
  381. function testSquaredDistance() {
  382. var rect = new goog.math.Rect(-10, -20, 15, 25);
  383. // Test regions:
  384. // 1 2 3
  385. // +-+
  386. // 4 |5| 6
  387. // +-+
  388. // 7 8 9
  389. // Region 5 (inside the rectangle).
  390. assertEquals(0, rect.squaredDistance(new goog.math.Coordinate(-10, 5)));
  391. assertEquals(0, rect.squaredDistance(new goog.math.Coordinate(5, -20)));
  392. // 1, 2, and 3.
  393. assertEquals(25, rect.squaredDistance(new goog.math.Coordinate(9, 8)));
  394. assertEquals(36, rect.squaredDistance(new goog.math.Coordinate(2, 11)));
  395. assertEquals(53, rect.squaredDistance(new goog.math.Coordinate(12, 7)));
  396. // 4 and 6.
  397. assertEquals(81, rect.squaredDistance(new goog.math.Coordinate(-19, -10)));
  398. assertEquals(64, rect.squaredDistance(new goog.math.Coordinate(13, 0)));
  399. // 7, 8, and 9.
  400. assertEquals(20, rect.squaredDistance(new goog.math.Coordinate(-12, -24)));
  401. assertEquals(9, rect.squaredDistance(new goog.math.Coordinate(0, -23)));
  402. assertEquals(34, rect.squaredDistance(new goog.math.Coordinate(8, -25)));
  403. }
  404. function testDistance() {
  405. var rect = new goog.math.Rect(2, 4, 8, 16);
  406. // Region 5 (inside the rectangle).
  407. assertEquals(0, rect.distance(new goog.math.Coordinate(2, 4)));
  408. assertEquals(0, rect.distance(new goog.math.Coordinate(10, 20)));
  409. // 1, 2, and 3.
  410. assertRoughlyEquals(
  411. Math.sqrt(8), rect.distance(new goog.math.Coordinate(0, 22)), .0001);
  412. assertEquals(8, rect.distance(new goog.math.Coordinate(9, 28)));
  413. assertRoughlyEquals(
  414. Math.sqrt(50), rect.distance(new goog.math.Coordinate(15, 25)), .0001);
  415. // 4 and 6.
  416. assertEquals(7, rect.distance(new goog.math.Coordinate(-5, 6)));
  417. assertEquals(10, rect.distance(new goog.math.Coordinate(20, 10)));
  418. // 7, 8, and 9.
  419. assertEquals(5, rect.distance(new goog.math.Coordinate(-2, 1)));
  420. assertEquals(2, rect.distance(new goog.math.Coordinate(5, 2)));
  421. assertRoughlyEquals(
  422. Math.sqrt(10), rect.distance(new goog.math.Coordinate(1, 1)), .0001);
  423. }