RegionTests.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. namespace Terminal.Gui.DrawingTests;
  2. public class RegionTests
  3. {
  4. [Fact]
  5. public void Clone_CreatesExactCopy ()
  6. {
  7. var region = new Region (new (10, 10, 50, 50));
  8. Region clone = region.Clone ();
  9. Assert.True (clone.Contains (20, 20));
  10. Assert.Equal (region.GetRectangles (), clone.GetRectangles ());
  11. }
  12. [Fact]
  13. public void Combine_EmptyRectangles_ProducesEmptyRegion ()
  14. {
  15. // Arrange: Create region and combine with empty rectangles
  16. var region = new Region ();
  17. region.Combine (new Rectangle (0, 0, 0, 0), RegionOp.Union); // Empty rectangle
  18. region.Combine (new Region (), RegionOp.Union); // Empty region
  19. // Assert: Region is empty
  20. Assert.Empty (region.GetRectangles ());
  21. }
  22. [Fact]
  23. public void Complement_Rectangle_ComplementsRegion ()
  24. {
  25. var region = new Region (new (10, 10, 50, 50));
  26. region.Complement (new (0, 0, 100, 100));
  27. Assert.True (region.Contains (5, 5));
  28. Assert.False (region.Contains (20, 20));
  29. }
  30. [Theory]
  31. [MemberData (nameof (Complement_TestData))]
  32. public void Complement_Region_Success (Region region, Rectangle [] rectangles, Rectangle [] expectedScans)
  33. {
  34. foreach (Rectangle rect in rectangles)
  35. {
  36. region.Complement (rect);
  37. }
  38. Rectangle [] actualScans = region.GetRectangles ();
  39. Assert.Equal (expectedScans, actualScans);
  40. }
  41. public static IEnumerable<object []> Complement_TestData ()
  42. {
  43. yield return new object []
  44. {
  45. new Region (new (10, 10, 100, 100)),
  46. new Rectangle [] { new (40, 60, 100, 20) },
  47. new Rectangle [] { new (110, 60, 30, 20) }
  48. };
  49. yield return new object []
  50. {
  51. new Region (new (70, 10, 100, 100)),
  52. new Rectangle [] { new (40, 60, 100, 20) },
  53. new Rectangle [] { new (40, 60, 30, 20) }
  54. };
  55. yield return new object []
  56. {
  57. new Region (new (40, 100, 100, 100)),
  58. new Rectangle [] { new (70, 80, 50, 40) },
  59. new Rectangle [] { new (70, 80, 50, 20) }
  60. };
  61. yield return new object []
  62. {
  63. new Region (new (40, 10, 100, 100)),
  64. new Rectangle [] { new (70, 80, 50, 40) },
  65. new Rectangle [] { new (70, 110, 50, 10) }
  66. };
  67. yield return new object []
  68. {
  69. new Region (new (30, 30, 80, 80)),
  70. new Rectangle []
  71. {
  72. new (45, 45, 200, 200),
  73. new (160, 260, 10, 10),
  74. new (170, 260, 10, 10)
  75. },
  76. new Rectangle [] { new (170, 260, 10, 10) }
  77. };
  78. yield return new object []
  79. {
  80. new Region (),
  81. new [] { Rectangle.Empty },
  82. new Rectangle[0]
  83. };
  84. yield return new object []
  85. {
  86. new Region (),
  87. new Rectangle [] { new (1, 2, 3, 4) },
  88. new Rectangle[0]
  89. };
  90. }
  91. [Fact]
  92. public void Complement_WithRectangle_ComplementsRegion ()
  93. {
  94. var region = new Region (new (10, 10, 50, 50));
  95. var rect = new Rectangle (0, 0, 100, 100);
  96. region.Complement (rect);
  97. // Points that were inside the original region should now be outside
  98. Assert.False (region.Contains (35, 35));
  99. // Points that were outside the original region but inside bounds should now be inside
  100. Assert.True (region.Contains (5, 5));
  101. Assert.True (region.Contains (95, 95));
  102. }
  103. [Fact]
  104. public void Complement_WithRegion_ComplementsRegion ()
  105. {
  106. var region = new Region (new (10, 10, 50, 50));
  107. var bounds = new Rectangle (0, 0, 100, 100);
  108. region.Complement (bounds);
  109. // Points that were inside the original region should now be outside
  110. Assert.False (region.Contains (35, 35));
  111. // Points that were outside the original region but inside bounds should now be inside
  112. Assert.True (region.Contains (5, 5));
  113. Assert.True (region.Contains (95, 95));
  114. }
  115. [Fact]
  116. public void Constructor_EmptyRegion_IsEmpty ()
  117. {
  118. var region = new Region ();
  119. Assert.True (region.IsEmpty ());
  120. }
  121. [Fact]
  122. public void Constructor_WithRectangle_IsNotEmpty ()
  123. {
  124. var region = new Region (new (10, 10, 50, 50));
  125. Assert.False (region.IsEmpty ());
  126. }
  127. [Fact]
  128. public void Contains_Point_ReturnsCorrectResult ()
  129. {
  130. var region = new Region (new (10, 10, 50, 50));
  131. Assert.True (region.Contains (20, 20));
  132. Assert.False (region.Contains (100, 100));
  133. }
  134. [Fact]
  135. public void Contains_PointInsideRegion_ReturnsTrue ()
  136. {
  137. var region = new Region (new (10, 10, 50, 50));
  138. Assert.True (region.Contains (20, 20));
  139. }
  140. [Fact]
  141. public void Contains_RectangleInsideRegion_ReturnsTrue ()
  142. {
  143. var region = new Region (new (10, 10, 50, 50));
  144. Assert.True (region.Contains (new (20, 20, 10, 10)));
  145. }
  146. [Fact]
  147. public void Equals_NullRegion_ReturnsFalse ()
  148. {
  149. var region = new Region ();
  150. Assert.False (region.Equals (null));
  151. }
  152. [Fact]
  153. public void Equals_SameRegion_ReturnsTrue ()
  154. {
  155. var region = new Region (new (1, 2, 3, 4));
  156. Assert.True (region.Equals (region));
  157. }
  158. public static IEnumerable<object []> Equals_TestData ()
  159. {
  160. static Region Empty ()
  161. {
  162. Region emptyRegion = new ();
  163. emptyRegion.Intersect (Rectangle.Empty);
  164. return emptyRegion;
  165. }
  166. yield return new object [] { new Region (), new Region (), true };
  167. yield return new object [] { new Region (), Empty (), true };
  168. yield return new object [] { new Region (), new Region (new (1, 2, 3, 4)), false };
  169. yield return new object [] { Empty (), Empty (), true };
  170. yield return new object [] { Empty (), new Region (new (0, 0, 0, 0)), true };
  171. yield return new object [] { Empty (), new Region (new (1, 2, 3, 3)), false };
  172. yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 2, 3, 4)), true };
  173. yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (2, 2, 3, 4)), false };
  174. yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 3, 3, 4)), false };
  175. yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 2, 4, 4)), false };
  176. yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 2, 3, 5)), false };
  177. }
  178. [Theory]
  179. [MemberData (nameof (Equals_TestData))]
  180. public void Equals_Valid_ReturnsExpected (Region region1, Region region2, bool expected) { Assert.Equal (expected, region1.Equals (region2)); }
  181. [Fact]
  182. public void GetBounds_ReturnsBoundingRectangle ()
  183. {
  184. var region = new Region (new (10, 10, 50, 50));
  185. region.Union (new Rectangle (100, 100, 20, 20));
  186. Rectangle bounds = region.GetBounds ();
  187. Assert.Equal (new (10, 10, 110, 110), bounds);
  188. }
  189. [Fact]
  190. public void GetBounds_ReturnsCorrectBounds ()
  191. {
  192. var region = new Region ();
  193. region.Union (new Rectangle (10, 10, 50, 50));
  194. region.Union (new Rectangle (30, 30, 50, 50));
  195. Rectangle bounds = region.GetBounds ();
  196. Assert.Equal (new (10, 10, 70, 70), bounds);
  197. }
  198. [Fact]
  199. public void GetRegionScans_ReturnsAllRectangles ()
  200. {
  201. var region = new Region (new (10, 10, 50, 50));
  202. region.Union (new Rectangle (100, 100, 20, 20));
  203. Rectangle [] scans = region.GetRectangles ();
  204. Assert.Equal (2, scans.Length);
  205. Assert.Contains (new (10, 10, 50, 50), scans);
  206. Assert.Contains (new (100, 100, 20, 20), scans);
  207. }
  208. [Fact]
  209. public void Intersect_Rectangle_IntersectsRegion ()
  210. {
  211. var region = new Region (new (10, 10, 50, 50));
  212. region.Intersect (new Rectangle (30, 30, 50, 50));
  213. Assert.False (region.Contains (20, 20));
  214. Assert.True (region.Contains (40, 40));
  215. }
  216. [Fact]
  217. public void Intersect_Region_IntersectsRegions ()
  218. {
  219. var region1 = new Region (new (10, 10, 50, 50));
  220. var region2 = new Region (new (30, 30, 50, 50));
  221. region1.Intersect (region2);
  222. Assert.False (region1.Contains (20, 20));
  223. Assert.True (region1.Contains (40, 40));
  224. }
  225. [Fact]
  226. public void Intersect_WithEmptyRectangle_ResultsInEmptyRegion ()
  227. {
  228. // Arrange
  229. var region = new Region (new (0, 0, 10, 10));
  230. var rectangle = Rectangle.Empty; // Use Empty instead of 0-size
  231. // Act
  232. region.Intersect (rectangle);
  233. // Assert
  234. Assert.True (region.IsEmpty ());
  235. }
  236. [Theory]
  237. [InlineData (0, 0, 0, 0)] // Empty by zero size
  238. [InlineData (0, 0, 0, 10)] // Empty by zero width
  239. [InlineData (0, 0, 10, 0)] // Empty by zero height
  240. [InlineData (-5, -5, 0, 0)] // Empty by zero size at negative coords
  241. [InlineData (10, 10, -5, -5)] // Empty by negative size
  242. public void Intersect_WithEmptyRegion_ResultsInEmptyRegion (int x, int y, int width, int height)
  243. {
  244. // Arrange
  245. var region = new Region ();
  246. region.Union (new Rectangle (0, 0, 10, 10));
  247. region.Union (new Rectangle (20, 0, 10, 10));
  248. // Create a region that should be considered empty
  249. var emptyRegion = new Region ();
  250. if (width <= 0 || height <= 0)
  251. {
  252. // For negative or zero dimensions, use an empty region
  253. emptyRegion = new ();
  254. }
  255. else
  256. {
  257. emptyRegion = new (new (x, y, width, height));
  258. }
  259. // Verify initial states
  260. Assert.Equal (2, region.GetRectangles ().Length);
  261. Assert.True (emptyRegion.IsEmpty ());
  262. // Act
  263. region.Intersect (emptyRegion);
  264. // Assert
  265. Assert.True (region.IsEmpty ());
  266. Assert.Empty (region.GetRectangles ());
  267. }
  268. [Fact]
  269. public void Intersect_WithFullyContainedRectangle_ResultsInSmallerRegion ()
  270. {
  271. // Arrange
  272. var region = new Region (new (0, 0, 10, 10));
  273. var rectangle = new Rectangle (2, 2, 4, 4);
  274. // Act
  275. region.Intersect (rectangle);
  276. // Assert
  277. Assert.Single (region.GetRectangles ());
  278. Assert.Equal (new (2, 2, 4, 4), region.GetRectangles () [0]);
  279. }
  280. [Fact]
  281. public void Intersect_WithMultipleRectanglesInRegion_IntersectsAll ()
  282. {
  283. // Arrange
  284. var region = new Region ();
  285. region.Union (new Rectangle (0, 0, 5, 5));
  286. region.Union (new Rectangle (10, 0, 5, 5));
  287. var rectangle = new Rectangle (2, 2, 10, 2);
  288. // Act
  289. region.Intersect (rectangle);
  290. // Assert
  291. Assert.Equal (2, region.GetRectangles ().Length);
  292. Assert.Contains (new (2, 2, 3, 2), region.GetRectangles ());
  293. Assert.Contains (new (10, 2, 2, 2), region.GetRectangles ());
  294. }
  295. //[Fact]
  296. //public void Intersect_WithEmptyRegion_ResultsInEmptyRegion ()
  297. //{
  298. // // Arrange
  299. // var region = new Region ();
  300. // var rectangle = new Rectangle (0, 0, 10, 10);
  301. // // Act
  302. // region.Intersect (rectangle);
  303. // // Assert
  304. // Assert.True (region.IsEmpty ());
  305. //}
  306. [Fact]
  307. public void Intersect_WithNonOverlappingRectangle_ResultsInEmptyRegion ()
  308. {
  309. // Arrange
  310. var region = new Region (new (0, 0, 5, 5));
  311. var rectangle = new Rectangle (10, 10, 5, 5);
  312. // Act
  313. region.Intersect (rectangle);
  314. // Assert
  315. Assert.True (region.IsEmpty ());
  316. }
  317. [Fact]
  318. public void Intersect_WithNullRegion_ResultsInEmptyRegion ()
  319. {
  320. // Arrange
  321. var region = new Region ();
  322. region.Union (new Rectangle (0, 0, 10, 10));
  323. region.Union (new Rectangle (20, 0, 10, 10));
  324. // Verify initial state
  325. Assert.Equal (2, region.GetRectangles ().Length);
  326. // Act
  327. region.Intersect (null);
  328. // Assert
  329. Assert.True (region.IsEmpty ());
  330. Assert.Empty (region.GetRectangles ());
  331. }
  332. [Fact]
  333. public void Intersect_WithPartiallyOverlappingRectangle_ResultsInIntersectedRegion ()
  334. {
  335. // Arrange
  336. var region = new Region (new (0, 0, 5, 5));
  337. var rectangle = new Rectangle (2, 2, 5, 5);
  338. // Act
  339. region.Intersect (rectangle);
  340. // Assert
  341. Assert.Single (region.GetRectangles ());
  342. Assert.Equal (new (2, 2, 3, 3), region.GetRectangles () [0]);
  343. }
  344. [Fact]
  345. public void Intersect_WithRectangle_IntersectsRectangles ()
  346. {
  347. var region = new Region (new (10, 10, 50, 50));
  348. var rect = new Rectangle (30, 30, 50, 50);
  349. region.Intersect (rect);
  350. Assert.True (region.Contains (35, 35));
  351. Assert.False (region.Contains (20, 20));
  352. }
  353. [Fact]
  354. public void Intersect_WithRegion_IntersectsRegions ()
  355. {
  356. var region1 = new Region (new (10, 10, 50, 50));
  357. var region2 = new Region (new (30, 30, 50, 50));
  358. region1.Intersect (region2.GetBounds ());
  359. Assert.True (region1.Contains (35, 35));
  360. Assert.False (region1.Contains (20, 20));
  361. }
  362. [Fact]
  363. public void Intersect_ImmediateNormalization_AffectsRectangleOrder ()
  364. {
  365. // Create a region with two overlapping rectangles
  366. var region1 = new Region (new (0, 0, 4, 4)); // 0,0 to 4,4
  367. // Intersect with a region that partially overlaps
  368. var region2 = new Region (new (2, 2, 4, 4)); // 2,2 to 6,6
  369. region1.Intersect (region2);
  370. // Get the resulting rectangles
  371. Rectangle [] result = region1.GetRectangles ();
  372. // Expected behavior from original Region:
  373. // Intersect immediately produces a single rectangle (2,2,2,2)
  374. Assert.Single (result); // Original has 1 rectangle due to immediate processing
  375. Assert.Equal (new (2, 2, 2, 2), result [0]);
  376. // My updated Region defers normalization after Intersect,
  377. // so GetRectangles() might merge differently or preserve order differently,
  378. // potentially failing the exact match or count due to _isDirty
  379. }
  380. [Fact]
  381. public void IsEmpty_AfterClear_ReturnsTrue ()
  382. {
  383. // Arrange
  384. var region = new Region (new (0, 0, 10, 10));
  385. // Act
  386. region.Intersect (Rectangle.Empty);
  387. // Assert
  388. Assert.True (region.IsEmpty ());
  389. Assert.Empty (region.GetRectangles ());
  390. }
  391. [Fact]
  392. public void IsEmpty_AfterComplement_ReturnsCorrectState ()
  393. {
  394. // Test 1: Complement a region with bounds that fully contain it
  395. var region = new Region (new (2, 2, 5, 5)); // Small inner rectangle
  396. region.Complement (new (0, 0, 10, 10)); // Larger outer bounds
  397. Assert.False (region.IsEmpty ()); // Should have area around the original rectangle
  398. // Test 2: Complement with bounds equal to the region
  399. region = new (new (0, 0, 10, 10));
  400. region.Complement (new (0, 0, 10, 10));
  401. Assert.True (region.IsEmpty ()); // Should be empty as there's no area left
  402. // Test 3: Complement with empty bounds
  403. region = new (new (0, 0, 10, 10));
  404. region.Complement (Rectangle.Empty);
  405. Assert.True (region.IsEmpty ()); // Should be empty as there's no bounds
  406. }
  407. [Fact]
  408. public void IsEmpty_AfterExclude_ReturnsTrue ()
  409. {
  410. // Arrange
  411. var region = new Region (new (0, 0, 10, 10));
  412. // Act
  413. region.Exclude (new Rectangle (0, 0, 10, 10));
  414. // Assert
  415. Assert.True (region.IsEmpty ());
  416. Assert.Empty (region.GetRectangles ());
  417. }
  418. [Fact]
  419. public void IsEmpty_AfterUnion_ReturnsFalse ()
  420. {
  421. // Arrange
  422. var region = new Region ();
  423. region.Union (new Rectangle (0, 0, 10, 10));
  424. // Assert
  425. Assert.False (region.IsEmpty ());
  426. Assert.Single (region.GetRectangles ());
  427. }
  428. [Fact]
  429. public void IsEmpty_EmptyRegion_ReturnsTrue ()
  430. {
  431. var region = new Region ();
  432. Assert.True (region.IsEmpty ());
  433. }
  434. [Fact]
  435. public void IsEmpty_MultipleOperations_ReturnsExpectedResult ()
  436. {
  437. // Arrange
  438. var region = new Region ();
  439. // Act & Assert - Should be empty initially
  440. Assert.True (region.IsEmpty ());
  441. // Add a rectangle - Should not be empty
  442. region.Union (new Rectangle (0, 0, 10, 10));
  443. Assert.False (region.IsEmpty ());
  444. // Exclude the same rectangle - Should be empty again
  445. region.Exclude (new Rectangle (0, 0, 10, 10));
  446. Assert.True (region.IsEmpty ());
  447. // Add two rectangles - Should not be empty
  448. region.Union (new Rectangle (0, 0, 5, 5));
  449. region.Union (new Rectangle (10, 10, 5, 5));
  450. Assert.False (region.IsEmpty ());
  451. }
  452. [Fact]
  453. public void IsEmpty_NewRegion_ReturnsTrue ()
  454. {
  455. // Arrange
  456. var region = new Region ();
  457. // Act & Assert
  458. Assert.True (region.IsEmpty ());
  459. Assert.Empty (region.GetRectangles ());
  460. }
  461. [Fact]
  462. public void IsEmpty_ReturnsCorrectResult ()
  463. {
  464. var region = new Region ();
  465. Assert.True (region.IsEmpty ());
  466. region.Union (new Rectangle (10, 10, 50, 50));
  467. Assert.False (region.IsEmpty ());
  468. }
  469. [Theory]
  470. [InlineData (0, 0, 1, 1)] // 1x1 at origin
  471. [InlineData (10, 10, 5, 5)] // 5x5 at (10,10)
  472. [InlineData (-5, -5, 10, 10)] // Negative coordinates
  473. public void IsEmpty_ValidRectangle_ReturnsFalse (int x, int y, int width, int height)
  474. {
  475. // Arrange
  476. var region = new Region (new (x, y, width, height));
  477. // Assert
  478. Assert.False (region.IsEmpty ());
  479. Assert.Single (region.GetRectangles ());
  480. }
  481. [Theory]
  482. [InlineData (0, 0, 0, 0)] // Zero size
  483. [InlineData (0, 0, 0, 10)] // Zero width
  484. [InlineData (0, 0, 10, 0)] // Zero height
  485. [InlineData (-5, -5, 0, 0)] // Zero size at negative coords
  486. public void IsEmpty_ZeroSizeRectangle_ReturnsCorrectState (int x, int y, int width, int height)
  487. {
  488. var region = new Region (new (x, y, width, height));
  489. // Only check IsEmpty() since Rectangle(0,0,0,0) is still stored
  490. Assert.True (region.IsEmpty ());
  491. }
  492. //[Fact]
  493. //public void MinimalUnion_SingleRectangle_DoesNotChange ()
  494. //{
  495. // var region = new Region (new Rectangle (0, 0, 10, 10));
  496. // region.MinimalUnion (new Rectangle (0, 0, 10, 10));
  497. // Assert.Single (region.GetRectangles ());
  498. // Assert.Equal (new Rectangle (0, 0, 10, 10), region.GetRectangles ().First ());
  499. //}
  500. //[Fact]
  501. //public void MinimalUnion_OverlappingRectangles_MergesIntoOne ()
  502. //{
  503. // var region = new Region (new Rectangle (0, 0, 10, 10));
  504. // region.MinimalUnion (new Rectangle (5, 0, 10, 10));
  505. // Assert.Single (region.GetRectangles ());
  506. // Assert.Equal (new Rectangle (0, 0, 15, 10), region.GetRectangles ().First ());
  507. //}
  508. //[Fact]
  509. //public void MinimalUnion_AdjacentRectangles_MergesIntoOne ()
  510. //{
  511. // var region = new Region (new Rectangle (0, 0, 10, 10));
  512. // region.MinimalUnion (new Rectangle (10, 0, 10, 10));
  513. // Assert.Single (region.GetRectangles ());
  514. // Assert.Equal (new Rectangle (0, 0, 20, 10), region.GetRectangles ().First ());
  515. //}
  516. //[Fact]
  517. //public void MinimalUnion_SeparateRectangles_KeepsBoth ()
  518. //{
  519. // var region = new Region (new Rectangle (0, 0, 10, 10));
  520. // region.MinimalUnion (new Rectangle (20, 20, 10, 10));
  521. // Assert.Equal (2, region.GetRectangles ().Length);
  522. // Assert.Contains (new Rectangle (0, 0, 10, 10), region.GetRectangles ());
  523. // Assert.Contains (new Rectangle (20, 20, 10, 10), region.GetRectangles ());
  524. //}
  525. //[Fact]
  526. //public void MinimalUnion_ComplexMerging_ProducesMinimalSet ()
  527. //{
  528. // var region = new Region (new Rectangle (0, 0, 10, 10));
  529. // region.MinimalUnion (new Rectangle (10, 0, 10, 10));
  530. // region.MinimalUnion (new Rectangle (0, 10, 10, 10));
  531. // region.MinimalUnion (new Rectangle (10, 10, 10, 10));
  532. // Assert.Single (region.GetRectangles ());
  533. // Assert.Equal (new Rectangle (0, 0, 20, 20), region.GetRectangles ().First ());
  534. //}
  535. [Fact]
  536. public void Intersect_ViewportLimitsDrawnRegion ()
  537. {
  538. // Arrange: Create regions for viewport and drawn content
  539. var viewport = new Region (new Rectangle (0, 0, 100, 100)); // Viewport
  540. var drawnRegion = new Region (new Rectangle (50, 50, 200, 200)); // Larger drawn content
  541. // Act: Intersect drawn region with viewport
  542. drawnRegion.Intersect (viewport);
  543. // Assert: Drawn region should be limited to viewport
  544. var rectangles = drawnRegion.GetRectangles ();
  545. Assert.Single (rectangles);
  546. Assert.Equal (new Rectangle (50, 50, 50, 50), rectangles [0]); // Limited to viewport bounds
  547. }
  548. //[Fact]
  549. //public void MinimalUnion_HorizontalMerge_MergesToSingleRectangle ()
  550. //{
  551. // // Arrange: Create a region with a rectangle at (0,0,5,5)
  552. // var region = new Region (new Rectangle (0, 0, 5, 5));
  553. // // Act: Merge an adjacent rectangle on the right using MinimalUnion
  554. // region.MinimalUnion (new Rectangle (5, 0, 5, 5));
  555. // var result = region.GetRectangles ();
  556. // // Assert: Expect a single merged rectangle covering (0,0,10,5)
  557. // Assert.Single (result);
  558. // Assert.Equal (new Rectangle (0, 0, 10, 5), result [0]);
  559. //}
  560. //[Fact]
  561. //public void MinimalUnion_VerticalMerge_MergesToSingleRectangle ()
  562. //{
  563. // // Arrange: Create a region with a rectangle at (0,0,5,5)
  564. // var region = new Region (new Rectangle (0, 0, 5, 5));
  565. // // Act: Merge an adjacent rectangle below using MinimalUnion
  566. // region.MinimalUnion (new Rectangle (0, 5, 5, 5));
  567. // var result = region.GetRectangles ();
  568. // // Assert: Expect a single merged rectangle covering (0,0,5,10)
  569. // Assert.Single (result);
  570. // Assert.Equal (new Rectangle (0, 0, 5, 10), result [0]);
  571. //}
  572. //[Fact]
  573. //public void MinimalUnion_OverlappingMerge_MergesToSingleRectangle ()
  574. //{
  575. // // Arrange: Create a region with a rectangle that overlaps with the next one horizontally
  576. // var region = new Region (new Rectangle (0, 0, 6, 5));
  577. // // Act: Merge an overlapping rectangle using MinimalUnion
  578. // region.MinimalUnion (new Rectangle (4, 0, 6, 5));
  579. // var result = region.GetRectangles ();
  580. // // Assert: Expect a single merged rectangle covering (0,0,10,5)
  581. // Assert.Single (result);
  582. // Assert.Equal (new Rectangle (0, 0, 10, 5), result [0]);
  583. //}
  584. //[Fact]
  585. //public void MinimalUnion_NonAdjacentRectangles_NoMergeOccurs ()
  586. //{
  587. // // Arrange: Create a region with one rectangle
  588. // var region = new Region (new Rectangle (0, 0, 5, 5));
  589. // // Act: Merge with a rectangle that does not touch the first
  590. // region.MinimalUnion (new Rectangle (6, 0, 5, 5));
  591. // var result = region.GetRectangles ();
  592. // // Assert: Expect two separate rectangles since they are not adjacent
  593. // Assert.Equal (2, result.Length);
  594. // Assert.Contains (new Rectangle (0, 0, 5, 5), result);
  595. // Assert.Contains (new Rectangle (6, 0, 5, 5), result);
  596. //}
  597. //[Fact]
  598. //public void MinimalUnion_MultipleMerge_FormsSingleContiguousRectangle ()
  599. //{
  600. // // Arrange: Four small rectangles that form a contiguous 6x6 block
  601. // var region = new Region (new Rectangle (0, 0, 3, 3));
  602. // // Act: Merge adjacent rectangles one by one using MinimalUnion
  603. // region.MinimalUnion (new Rectangle (3, 0, 3, 3)); // Now covers (0,0,6,3)
  604. // region.MinimalUnion (new Rectangle (0, 3, 3, 3)); // Add bottom-left
  605. // region.MinimalUnion (new Rectangle (3, 3, 3, 3)); // Add bottom-right to complete block
  606. // var result = region.GetRectangles ();
  607. // // Assert: Expect a single merged rectangle covering (0,0,6,6)
  608. // Assert.Single (result);
  609. // Assert.Equal (new Rectangle (0, 0, 6, 6), result [0]);
  610. //}
  611. [Fact]
  612. public void Translate_EmptyRegionAfterEmptyCombine_NoEffect ()
  613. {
  614. // Arrange: Create region and combine with empty rectangles
  615. var region = new Region ();
  616. region.Combine (new Rectangle (0, 0, 0, 0), RegionOp.Union); // Empty rectangle
  617. region.Combine (new Region (), RegionOp.Union); // Empty region
  618. // Act: Translate by (10, 20)
  619. region.Translate (10, 20);
  620. // Assert: Still empty
  621. Assert.Empty (region.GetRectangles ());
  622. }
  623. [Fact]
  624. public void Union_Rectangle_AddsToRegion ()
  625. {
  626. var region = new Region ();
  627. region.Union (new Rectangle (10, 10, 50, 50));
  628. Assert.False (region.IsEmpty ());
  629. Assert.True (region.Contains (20, 20));
  630. }
  631. [Fact]
  632. public void Union_Region_MergesRegions ()
  633. {
  634. var region1 = new Region (new (10, 10, 50, 50));
  635. var region2 = new Region (new (30, 30, 50, 50));
  636. region1.Union (region2);
  637. Assert.True (region1.Contains (20, 20));
  638. Assert.True (region1.Contains (40, 40));
  639. }
  640. /// <summary>
  641. /// Proves MergeRegion does not overly combine regions.
  642. /// </summary>
  643. [Fact]
  644. public void Union_Region_MergesRegions_NonOverlapping ()
  645. {
  646. // 012345
  647. // 0+++
  648. // 1+ +
  649. // 2+++
  650. // 3 ***
  651. // 4 * *
  652. // 5 ***
  653. var region1 = new Region (new (0, 0, 3, 3));
  654. var region2 = new Region (new (3, 3, 3, 3));
  655. region1.Union (region2);
  656. // Positive
  657. Assert.True (region1.Contains (0, 0));
  658. Assert.True (region1.Contains (1, 1));
  659. Assert.True (region1.Contains (2, 2));
  660. Assert.True (region1.Contains (4, 4));
  661. Assert.True (region1.Contains (5, 5));
  662. // Negative
  663. Assert.False (region1.Contains (0, 3));
  664. Assert.False (region1.Contains (3, 0));
  665. Assert.False (region1.Contains (6, 6));
  666. }
  667. /// <summary>
  668. /// Proves MergeRegion does not overly combine regions.
  669. /// </summary>
  670. [Fact]
  671. public void Union_Region_MergesRegions_Overlapping ()
  672. {
  673. // 01234567
  674. // 0+++++
  675. // 1+ +
  676. // 2+ +
  677. // 3+ *****
  678. // 4+++* *
  679. // 5 * *
  680. // 6 * *
  681. // 7 *****
  682. var region1 = new Region (new (0, 0, 5, 5));
  683. var region2 = new Region (new (3, 3, 5, 5));
  684. region1.Union (region2);
  685. // Positive
  686. Assert.True (region1.Contains (0, 0));
  687. Assert.True (region1.Contains (1, 1));
  688. Assert.True (region1.Contains (4, 4));
  689. Assert.True (region1.Contains (7, 7));
  690. // Negative
  691. Assert.False (region1.Contains (0, 5));
  692. Assert.False (region1.Contains (5, 0));
  693. Assert.False (region1.Contains (8, 8));
  694. Assert.False (region1.Contains (8, 8));
  695. }
  696. [Fact]
  697. public void Union_WithRectangle_AddsRectangle ()
  698. {
  699. var region = new Region ();
  700. var rect = new Rectangle (10, 10, 50, 50);
  701. region.Union (rect);
  702. Assert.True (region.Contains (20, 20));
  703. Assert.False (region.Contains (100, 100));
  704. }
  705. [Fact]
  706. public void Union_WithRegion_AddsRegion ()
  707. {
  708. var region1 = new Region (new (10, 10, 50, 50));
  709. var region2 = new Region (new (30, 30, 50, 50));
  710. region1.Union (region2.GetBounds ());
  711. Assert.True (region1.Contains (20, 20));
  712. Assert.True (region1.Contains (40, 40));
  713. }
  714. [Fact]
  715. public void Intersect_DeferredNormalization_PreservesSegments ()
  716. {
  717. var region = new Region (new (0, 0, 3, 1)); // Horizontal
  718. region.Union (new Rectangle (1, 0, 1, 2)); // Vertical
  719. region.Intersect (new Rectangle (0, 0, 2, 2)); // Clip
  720. Rectangle [] result = region.GetRectangles ();
  721. // Original & Updated (with normalization disabled) behavior:
  722. // Produces [(0,0,1,1), (1,0,1,2), (2,0,0,1)]
  723. Assert.Equal (3, result.Length);
  724. Assert.Contains (new (0, 0, 1, 1), result);
  725. Assert.Contains (new (1, 0, 1, 2), result);
  726. Assert.Contains (new (2, 0, 0, 1), result);
  727. }
  728. }