From 42176a58fc715f7da2183c7cc7f0a7d06ac97a69 Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Sun, 10 Nov 2024 16:22:57 +0500 Subject: [PATCH 1/7] complete tag cloud tasks --- .../CircularCloudLayouter.cs | 55 +++++++ cs/TagsCloudVisualization/Program.cs | 43 ++++++ cs/TagsCloudVisualization/README.md | 16 +++ .../RectangleLayouter.cs | 39 +++++ cs/TagsCloudVisualization/RectangleShifter.cs | 98 +++++++++++++ cs/TagsCloudVisualization/SpiralGenerator.cs | 41 ++++++ .../TagsCloudVisualization.csproj | 22 +++ cs/TagsCloudVisualization/layout1.png | Bin 0 -> 12702 bytes cs/TagsCloudVisualization/layout2.png | Bin 0 -> 11764 bytes cs/TagsCloudVisualization/layout3.png | Bin 0 -> 11843 bytes .../RectangleLayouterTests.cs | 55 +++++++ .../RectangleShifterTest.cs | 54 +++++++ .../SpiralGeneratorTests.cs | 105 ++++++++++++++ .../TagsCloudVisualizationTest.csproj | 23 +++ cs/TagsCloudVisualizationTest/Tests.cs | 136 ++++++++++++++++++ cs/tdd.sln | 12 ++ 16 files changed, 699 insertions(+) create mode 100644 cs/TagsCloudVisualization/CircularCloudLayouter.cs create mode 100644 cs/TagsCloudVisualization/Program.cs create mode 100644 cs/TagsCloudVisualization/README.md create mode 100644 cs/TagsCloudVisualization/RectangleLayouter.cs create mode 100644 cs/TagsCloudVisualization/RectangleShifter.cs create mode 100644 cs/TagsCloudVisualization/SpiralGenerator.cs create mode 100644 cs/TagsCloudVisualization/TagsCloudVisualization.csproj create mode 100644 cs/TagsCloudVisualization/layout1.png create mode 100644 cs/TagsCloudVisualization/layout2.png create mode 100644 cs/TagsCloudVisualization/layout3.png create mode 100644 cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs create mode 100644 cs/TagsCloudVisualizationTest/RectangleShifterTest.cs create mode 100644 cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs create mode 100644 cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj create mode 100644 cs/TagsCloudVisualizationTest/Tests.cs diff --git a/cs/TagsCloudVisualization/CircularCloudLayouter.cs b/cs/TagsCloudVisualization/CircularCloudLayouter.cs new file mode 100644 index 000000000..7b8283c82 --- /dev/null +++ b/cs/TagsCloudVisualization/CircularCloudLayouter.cs @@ -0,0 +1,55 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using TagsCloudVisualization; + +public class CircularCloudLayouter +{ + private readonly RectangleLayouter rectangleLayouter; + + public CircularCloudLayouter(Point center) + { + rectangleLayouter = new RectangleLayouter(center); + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + return rectangleLayouter.PutNextRectangle(rectangleSize); + } + + public List GetRectangles() + { + return rectangleLayouter.GetRectangles(); + } + + public void SaveImage(string filename, Size imageSize) + { + using var bitmap = new Bitmap(imageSize.Width, imageSize.Height); + using var graphics = Graphics.FromImage(bitmap); + graphics.Clear(Color.Black); + + var random = new Random(); + foreach (var rectangle in GetRectangles()) + { + var color = Color.FromArgb( + random.Next(128, 255), + random.Next(128, 255), + random.Next(128, 255) + ); + + using var brush = new SolidBrush(Color.FromArgb(180, color)); + using var pen = new Pen(color, 2f); + + var rect = new Rectangle( + rectangle.X + imageSize.Width / 2, + rectangle.Y + imageSize.Height / 2, + rectangle.Width, + rectangle.Height); + + graphics.FillRectangle(brush, rect); + graphics.DrawRectangle(pen, rect); + } + + bitmap.Save(filename, ImageFormat.Png); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Program.cs b/cs/TagsCloudVisualization/Program.cs new file mode 100644 index 000000000..295076347 --- /dev/null +++ b/cs/TagsCloudVisualization/Program.cs @@ -0,0 +1,43 @@ +using System.Drawing; + + +GenerateLayout1(); +GenerateLayout2(); +GenerateLayout3(); +return; + + +void GenerateLayout1() +{ + var layouter = new CircularCloudLayouter(new Point(0, 0)); + var random = new Random(); + for (var i = 0; i < 100; i++) + { + var size = new Size(random.Next(20, 100), random.Next(10, 50)); + layouter.PutNextRectangle(size); + } + layouter.SaveImage("layout1.png", new Size(800, 600)); +} + +void GenerateLayout2() +{ + var layouter = new CircularCloudLayouter(new Point(0, 0)); + for (var i = 0; i < 150; i++) + { + var size = new Size(50, 20); + layouter.PutNextRectangle(size); + } + layouter.SaveImage("layout2.png", new Size(800, 600)); +} + +void GenerateLayout3() +{ + var layouter = new CircularCloudLayouter(new Point(0, 0)); + var random = new Random(); + for (var i = 0; i < 200; i++) + { + var size = new Size(random.Next(10, 30), random.Next(10, 30)); + layouter.PutNextRectangle(size); + } + layouter.SaveImage("layout3.png", new Size(800, 600)); +} diff --git a/cs/TagsCloudVisualization/README.md b/cs/TagsCloudVisualization/README.md new file mode 100644 index 000000000..750579cab --- /dev/null +++ b/cs/TagsCloudVisualization/README.md @@ -0,0 +1,16 @@ +# Circular Cloud Layouter + + +### Раскладка 1 + +![Layout 1](layout1.png) + + +### Раскладка 2 + +![Layout 2](layout2.png) + + +### Раскладка 3 + +![Layout 3](layout3.png) \ No newline at end of file diff --git a/cs/TagsCloudVisualization/RectangleLayouter.cs b/cs/TagsCloudVisualization/RectangleLayouter.cs new file mode 100644 index 000000000..d850561cf --- /dev/null +++ b/cs/TagsCloudVisualization/RectangleLayouter.cs @@ -0,0 +1,39 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public class RectangleLayouter +{ + private readonly SpiralGenerator spiralGenerator; + private readonly RectangleShifter rectangleShifter; + private readonly List rectangles; + + public RectangleLayouter(Point center) + { + spiralGenerator = new SpiralGenerator(center); + rectangleShifter = new RectangleShifter(center); + rectangles = new List(); + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) + throw new ArgumentException("Размеры прямоугольника должны быть положительными."); + + Rectangle newRectangle; + do + { + var point = spiralGenerator.GetNextPoint(); + var location = new Point( + point.X - rectangleSize.Width / 2, + point.Y - rectangleSize.Height / 2); + newRectangle = new Rectangle(location, rectangleSize); + } while (rectangles.Any(r => r.IntersectsWith(newRectangle))); + + newRectangle = rectangleShifter.ShiftRectangleToCenter(newRectangle, rectangles); + rectangles.Add(newRectangle); + return newRectangle; + } + + public List GetRectangles() => rectangles; +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/RectangleShifter.cs b/cs/TagsCloudVisualization/RectangleShifter.cs new file mode 100644 index 000000000..dc57a2973 --- /dev/null +++ b/cs/TagsCloudVisualization/RectangleShifter.cs @@ -0,0 +1,98 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public class RectangleShifter +{ + private readonly Point center; + + public RectangleShifter(Point center) + { + this.center = center; + } + +public Rectangle ShiftRectangleToCenter(Rectangle rectangle, List rectangles) +{ + if (rectangles.Any(r => r.IntersectsWith(rectangle))) + { + rectangle = FindNearestNonIntersectingPosition(rectangle, rectangles); + if (rectangle == Rectangle.Empty) + throw new InvalidOperationException("Невозможно разместить прямоугольник без пересечений."); + } + + var shiftedRectangle = rectangle; + var directionToCenter = GetDirectionToCenter(shiftedRectangle); + + while (directionToCenter != Point.Empty) + { + var nextLocation = new Point( + shiftedRectangle.Location.X + Math.Sign(directionToCenter.X), + shiftedRectangle.Location.Y + Math.Sign(directionToCenter.Y)); + + var nextRectangle = new Rectangle(nextLocation, shiftedRectangle.Size); + + if (rectangles.Any(r => r.IntersectsWith(nextRectangle))) + break; + + shiftedRectangle = nextRectangle; + directionToCenter = GetDirectionToCenter(shiftedRectangle); + } + + return shiftedRectangle; +} + + private Rectangle FindNearestNonIntersectingPosition(Rectangle rectangle, List rectangles) + { + var queue = new Queue(); + var visited = new HashSet(); + queue.Enqueue(rectangle.Location); + visited.Add(rectangle.Location); + + var directions = new[] + { + new Point(-1, 0), + new Point(1, 0), + new Point(0, -1), + new Point(0, 1) + }; + + while (queue.Count > 0) + { + var currentLocation = queue.Dequeue(); + var currentRectangle = new Rectangle(currentLocation, rectangle.Size); + + if (!rectangles.Any(r => r.IntersectsWith(currentRectangle))) + return currentRectangle; + + foreach (var dir in directions) + { + var nextLocation = new Point( + currentLocation.X + dir.X, + currentLocation.Y + dir.Y); + + if (!visited.Contains(nextLocation)) + { + queue.Enqueue(nextLocation); + visited.Add(nextLocation); + } + } + } + + return Rectangle.Empty; + } + + private Point GetDirectionToCenter(Rectangle rectangle) + { + var rectangleCenter = new Point( + rectangle.Left + rectangle.Width / 2, + rectangle.Top + rectangle.Height / 2); + + var directionX = center.X - rectangleCenter.X; + var directionY = center.Y - rectangleCenter.Y; + + if (directionX == 0 && directionY == 0) + return Point.Empty; + + return new Point(directionX, directionY); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/SpiralGenerator.cs b/cs/TagsCloudVisualization/SpiralGenerator.cs new file mode 100644 index 000000000..590915cb8 --- /dev/null +++ b/cs/TagsCloudVisualization/SpiralGenerator.cs @@ -0,0 +1,41 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public class SpiralGenerator +{ + private readonly Point center; + private double phi; + private readonly double spiralStep; + private readonly double deltaPhi; + private Point? lastGeneratedPoint; + + public SpiralGenerator(Point center, double spiralStep = 1, double deltaPhiDegrees = (Math.PI / 180)) + { + if (spiralStep <= 0) + throw new ArgumentException("Spiral step must be greater than zero"); + + this.center = center; + phi = 0; + this.spiralStep = spiralStep; + deltaPhi = deltaPhiDegrees; + } + + public Point GetNextPoint() + { + Point newPoint; + + do + { + var radius = spiralStep * phi; + var x = (int)Math.Round(radius * Math.Cos(phi)) + center.X; + var y = (int)Math.Round(radius * Math.Sin(phi)) + center.Y; + phi += deltaPhi; + + newPoint = new Point(x, y); + } while (newPoint == lastGeneratedPoint); + + lastGeneratedPoint = newPoint; + return newPoint; + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/TagsCloudVisualization.csproj b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj new file mode 100644 index 000000000..ec8cbc62e --- /dev/null +++ b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + enable + Linux + + + + + + + + + + + + + + diff --git a/cs/TagsCloudVisualization/layout1.png b/cs/TagsCloudVisualization/layout1.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f866ae60a714a17c8e382ebc142cbdf826567a GIT binary patch literal 12702 zcmeHu2~-qUw{BHYY9nGAK@mkQXapY5gSmZZn*mtGy?kHn}6N=-n#GI>sl^XSDo5t z?{Dw3&)(lTZu`>1ReRuwff$BqyYXjwVVDMmVZ^tb{_rGq&BkE(2Vdpo>VzFDF_gm( zGT`$$pJUjGom$F8{ouD|FyDI>h7G=q{^G?!zX~vHqP5%1&%a)~pth{-mc94qvXvbk z+w~?M+3xbSrCBQHkBvvRYH7U;(bUReBt=8Rirk$BEtqSz-Pmm5cAqa2f4Dui|BkN~ z8;ogvSbDwbTxQClLv88uTZjC8-nKq%xZIRtTYhqNg{*OBhe`v(3V*($gJJtOPa`nw zHrExym=Tp64ExgA5XZ0|xAHKI)?P_s*sn$jpzM_YzXzAEla)qo{5h+JD13cIXa97$ z)AiNeKdMZdpMFxXZM-gp8E)gzVWea|$-!JJIX6e)IT_0aNMs$CIu2W^{4lc*U!Nwb ze{p=1SV?jU7q29rCt%(?G5jYOY$cX>26ZHO!Lnj?J6;bIABWQc+KHiYAviSkx(g$#DC|>Sf2A7;N%{1gu<#da3gq&Y4EzUbEI`<2GYE2g@hY867`)`o?3poo<(z!lfkV zW{O&_1&?M*)Mg6|a3a)9Jo*b#{h+}`O0F_t?F_Yy1W z2JGM8Dbd`;X->GNaQ=nr+Hc_doX)2K_HB*6KXNG~vg?s6^S7s-#bEo?ew9oj$mg@w z&P&pv=m53lIXnI(D+^2VTce`#eX7FrkOR7`U1R3=D12h1E1okN?-IB$KzyyWA*w!c zMfyPQ-G*>~rOG%#Rz8z8;A$o3)eh{*2(Q`KS1Mndm2reqcE~&#Y^1*H!hm;NYE^u4 zg?Yu>HS^5q&2{E96CcMrZcS8A#-$1F8lr=ZVc9NSgGossAsR&ps9u8(R)R4Tv~^vx%-XkJjw1cY?xORCCJwpHGmg zm>$)v@N*CFx;$E=daCTr?%g|*7d%MJsL4=6Fq!9fwaH|kn>JB>Dir_TPrmN;Hsl+y z%E?+B*PNEQUNE1Ogv`~bKCz{J==kBKjt(PBbi3gCVKeq;jaMr6`aL$OnyE>3j)*q@UOK-Hz{=ApO?ORQyMd$V9$`u^pYE5B_8roDNx;}TulQ+-w z^LO#8az)nRLd?7D5w@;e*UEn%78t|&pH)DIO2O_!tYvnaP4Z{x=ZRUkHVY@j(bqIDMNtozT4|gx-=}-fD`G@4Mabxy;wje?FrX=9k*^J>1{h
MXYGOo2bzHRwkhbl9o|Mn+gjtaqQ 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 NZY~}(k2x*e_-{o92{`}& literal 0 HcmV?d00001 diff --git a/cs/TagsCloudVisualization/layout2.png b/cs/TagsCloudVisualization/layout2.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8a183ee56517d28e5e51270002c89928b7a528 GIT binary patch literal 11764 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 literal 0 HcmV?d00001 diff --git a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs new file mode 100644 index 000000000..116277219 --- /dev/null +++ b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs @@ -0,0 +1,55 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using TagsCloudVisualization; + +namespace TagsCloudVisualizationTest; + +[TestFixture] +public class RectangleLayouterTests +{ + private RectangleLayouter layouter; + + [SetUp] + public void SetUp() + { + layouter = new RectangleLayouter(new Point(0, 0)); + } + + [Test] + public void PutNextRectangle_FirstRectangle_ShouldBeAtCenter() + { + var size = new Size(10, 10); + var rectangle = layouter.PutNextRectangle(size); + + var expectedLocation = new Point(-5, -5); + rectangle.Location.Should().Be(expectedLocation); + } + + [Test] + public void PutNextRectangle_RectanglesShouldNotOverlap() + { + var sizes = new[] + { + new Size(50, 10), + new Size(10, 20), + new Size(20, 20), + new Size(30, 10), + new Size(10, 40) + }; + + foreach (var size in sizes) + layouter.PutNextRectangle(size); + + var rectangles = layouter.GetRectangles(); + + for (int i = 0; i < rectangles.Count; i++) + { + for (int j = i + 1; j < rectangles.Count; j++) + { + rectangles[i].IntersectsWith(rectangles[j]) + .Should().BeFalse($"Прямоугольники {i} и {j} пересекаются."); + } + } + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/RectangleShifterTest.cs b/cs/TagsCloudVisualizationTest/RectangleShifterTest.cs new file mode 100644 index 000000000..29788d657 --- /dev/null +++ b/cs/TagsCloudVisualizationTest/RectangleShifterTest.cs @@ -0,0 +1,54 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using TagsCloudVisualization; + +namespace TagsCloudVisualizationTest; + +[TestFixture] +public class RectangleShifterTests +{ + private RectangleShifter shifter; + private Point center; + + [SetUp] + public void SetUp() + { + center = new Point(0, 0); + shifter = new RectangleShifter(center); + } + + [Test] + public void ShiftRectangleToCenter_ShouldMoveRectangleCloserToCenter() + { + var rectangle = new Rectangle(10, 10, 5, 5); + var rectangles = new List(); + + var shiftedRectangle = shifter.ShiftRectangleToCenter(rectangle, rectangles); + + GetDistanceToCenter(shiftedRectangle).Should().BeLessThan(GetDistanceToCenter(rectangle)); + } + + [Test] + public void ShiftRectangleToCenter_ShouldNotIntersectWithExistingRectangles() + { + var existingRectangle = new Rectangle(0, 0, 10, 10); + var rectangle = new Rectangle(5, 5, 10, 10); + var rectangles = new List { existingRectangle }; + + var shiftedRectangle = shifter.ShiftRectangleToCenter(rectangle, rectangles); + + shiftedRectangle.IntersectsWith(existingRectangle).Should().BeFalse(); + } + + private double GetDistanceToCenter(Rectangle rect) + { + var rectangleCenter = new Point( + rect.Left + rect.Width / 2, + rect.Top + rect.Height / 2); + + var dx = rectangleCenter.X - center.X; + var dy = rectangleCenter.Y - center.Y; + return Math.Sqrt(dx * dx + dy * dy); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs new file mode 100644 index 000000000..4e4465f3d --- /dev/null +++ b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs @@ -0,0 +1,105 @@ +using NUnit.Framework; +using FluentAssertions; +using System.Drawing; +using TagsCloudVisualization; + +namespace TagsCloudVisualizationTest; + +[TestFixture] +public class SpiralGeneratorTests +{ + private Point center; + private SpiralGenerator generator; + + [SetUp] + public void Setup() + { + center = new Point(0, 0); + generator = new SpiralGenerator(center); + } + + [Test] + public void Constructor_WithNegativeSpiralStep_ThrowsArgumentException() + { + Action act = () => new SpiralGenerator(center, -1); + + act.Should().Throw() + .WithMessage("Spiral step must be greater than zero"); + } + + [Test] + public void GetNextPoint_FirstPoint_ShouldBeAtCenter() + { + var firstPoint = generator.GetNextPoint(); + + firstPoint.Should().Be(center); + } + + [Test] + public void GetNextPoint_ShouldGenerateUniquePoints() + { + var points = new HashSet(); + const int numberOfPoints = 100; + + for (int i = 0; i < numberOfPoints; i++) + { + points.Add(generator.GetNextPoint()); + } + + points.Count.Should().Be(numberOfPoints, + "все точки должны быть уникальными"); + } + + [Test] + public void GetNextPoint_ShouldGeneratePointsWithIncreasingDistanceFromCenter() + { + const int numberOfPoints = 50; + var initialPoint = generator.GetNextPoint(); + var initialDistance = GetDistanceFromCenter(initialPoint); + var halfwayPoint = numberOfPoints / 2; + + // Генерируем половину точек + for (int i = 0; i < halfwayPoint; i++) + { + generator.GetNextPoint(); + } + var middlePoint = generator.GetNextPoint(); + var middleDistance = GetDistanceFromCenter(middlePoint); + + // Генерируем оставшиеся точки + for (int i = halfwayPoint; i < numberOfPoints; i++) + { + generator.GetNextPoint(); + } + var finalPoint = generator.GetNextPoint(); + var finalDistance = GetDistanceFromCenter(finalPoint); + + finalDistance.Should().BeGreaterThan(middleDistance) + .And.BeGreaterThan(initialDistance); + } + + [Test] + public void GetNextPoint_ShouldNotGenerateConsecutiveDuplicatePoints() + { + const int numberOfPoints = 100; + Point? previousPoint = null; + + for (int i = 0; i < numberOfPoints; i++) + { + var currentPoint = generator.GetNextPoint(); + if (previousPoint != null) + { + currentPoint.Should().NotBe(previousPoint, + "последовательные точки не должны повторяться"); + } + previousPoint = currentPoint; + } + } + + private double GetDistanceFromCenter(Point point) + { + var dx = point.X - center.X; + var dy = point.Y - center.Y; + return Math.Sqrt(dx * dx + dy * dy); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj b/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj new file mode 100644 index 000000000..d300c3912 --- /dev/null +++ b/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/cs/TagsCloudVisualizationTest/Tests.cs b/cs/TagsCloudVisualizationTest/Tests.cs new file mode 100644 index 000000000..d7451409d --- /dev/null +++ b/cs/TagsCloudVisualizationTest/Tests.cs @@ -0,0 +1,136 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using NUnit.Framework.Interfaces; + +namespace TagsCloudVisualizationTest; + +[TestFixture] +public class CircularCloudLayouterTests +{ + private CircularCloudLayouter layouter; + private Point center; + + [SetUp] + public void SetUp() + { + center = new Point(0, 0); + layouter = new CircularCloudLayouter(center); + } + + [Test] + public void PutNextRectangle_FirstRectangle_ShouldBePlacedAtCenter() + { + var size = new Size(10, 10); + var rectangle = layouter.PutNextRectangle(size); + + var expectedLocation = new Point(center.X - size.Width / 2, center.Y - size.Height / 2); + rectangle.Location.Should().Be(expectedLocation); + } + + [Test] + public void PutNextRectangle_RectanglesShouldNotOverlap() + { + var sizes = new[] + { + new Size(50, 10), + new Size(10, 20), + new Size(20, 20), + new Size(30, 10), + new Size(10, 40) + }; + + foreach (var size in sizes) + { + layouter.PutNextRectangle(size); + } + + var rectangles = layouter.GetRectangles(); + + for (var i = 0; i < rectangles.Count; i++) + { + for (var j = i + 1; j < rectangles.Count; j++) + { + rectangles[i].IntersectsWith(rectangles[j]) + .Should().BeFalse($"Прямоугольники {i} и {j} пересекаются."); + } + } + } + + [Test] + public void PutNextRectangle_RectanglesShouldFormACircleShape() + { + var sizes = Enumerable.Repeat(new Size(10, 10), 100).ToList(); + + foreach (var size in sizes) + { + layouter.PutNextRectangle(size); + } + + var rectangles = layouter.GetRectangles(); + var distances = rectangles + .Select(r => GetDistanceToCenter(GetRectangleCenter(r), center)) + .ToList(); + + var averageDistance = distances.Average(); + var maxDistance = distances.Max(); + + maxDistance.Should().BeLessOrEqualTo(averageDistance * 2, "Прямоугольники расположены не компактно."); + } + + [Test] + public void GenerateLayout_ShouldCreateCircularCloud() + { + var random = new Random(); + for (var i = 0; i < 100; i++) + { + var size = new Size(random.Next(10, 50), random.Next(10, 50)); + layouter.PutNextRectangle(size); + } + + var rectangles = layouter.GetRectangles(); + rectangles.Count.Should().Be(100); + + var distances = rectangles + .Select(r => GetDistanceToCenter(GetRectangleCenter(r), center)) + .ToList(); + + var averageDistance = distances.Average(); + var maxDistance = distances.Max(); + + maxDistance.Should().BeLessOrEqualTo(averageDistance * 2, "Облако не является компактным."); + } + + private Point GetRectangleCenter(Rectangle rectangle) + { + return new Point( + rectangle.Left + rectangle.Width / 2, + rectangle.Top + rectangle.Height / 2); + } + + private double GetDistanceToCenter(Point point, Point center) + { + var dx = point.X - center.X; + var dy = point.Y - center.Y; + return Math.Sqrt(dx * dx + dy * dy); + } + + [TearDown] + public void TearDown() + { + if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed) + { + return; + } + + var testName = TestContext.CurrentContext.Test.Name; + var directory = Path.Combine(TestContext.CurrentContext.WorkDirectory, "FailedTests"); + + Directory.CreateDirectory(directory); + + var filePath = Path.Combine(directory, $"{testName}.png"); + + layouter.SaveImage(filePath, new Size(800, 600)); + Console.WriteLine($"Tag cloud visualization saved to file {filePath}"); + } +} \ No newline at end of file diff --git a/cs/tdd.sln b/cs/tdd.sln index c8f523d63..0f14101d9 100644 --- a/cs/tdd.sln +++ b/cs/tdd.sln @@ -7,6 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BowlingGame", "BowlingGame\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{B5108E20-2ACF-4ED9-84FE-2A718050FC94}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudVisualization", "TagsCloudVisualization\TagsCloudVisualization.csproj", "{16DA30D3-ABC2-4F4F-B453-F464AEA4116F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudVisualizationTest", "TagsCloudVisualizationTest\TagsCloudVisualizationTest.csproj", "{1CE5A2CB-C60E-4158-8686-2FAB0C81147A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +25,14 @@ Global {B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Release|Any CPU.ActiveCfg = Release|Any CPU {B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Release|Any CPU.Build.0 = Release|Any CPU + {16DA30D3-ABC2-4F4F-B453-F464AEA4116F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16DA30D3-ABC2-4F4F-B453-F464AEA4116F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16DA30D3-ABC2-4F4F-B453-F464AEA4116F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16DA30D3-ABC2-4F4F-B453-F464AEA4116F}.Release|Any CPU.Build.0 = Release|Any CPU + {1CE5A2CB-C60E-4158-8686-2FAB0C81147A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CE5A2CB-C60E-4158-8686-2FAB0C81147A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CE5A2CB-C60E-4158-8686-2FAB0C81147A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CE5A2CB-C60E-4158-8686-2FAB0C81147A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From dabe01763c4bd189a94efeffecf53dc461fb0be3 Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Sun, 10 Nov 2024 16:25:33 +0500 Subject: [PATCH 2/7] refactoring --- cs/TagsCloudVisualization/RectangleLayouter.cs | 15 ++++----------- cs/TagsCloudVisualization/SpiralGenerator.cs | 2 +- .../RectangleLayouterTests.cs | 4 ++-- .../SpiralGeneratorTests.cs | 17 +++++++++-------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/cs/TagsCloudVisualization/RectangleLayouter.cs b/cs/TagsCloudVisualization/RectangleLayouter.cs index d850561cf..e79666af3 100644 --- a/cs/TagsCloudVisualization/RectangleLayouter.cs +++ b/cs/TagsCloudVisualization/RectangleLayouter.cs @@ -2,18 +2,11 @@ namespace TagsCloudVisualization; -public class RectangleLayouter +public class RectangleLayouter(Point center) { - private readonly SpiralGenerator spiralGenerator; - private readonly RectangleShifter rectangleShifter; - private readonly List rectangles; - - public RectangleLayouter(Point center) - { - spiralGenerator = new SpiralGenerator(center); - rectangleShifter = new RectangleShifter(center); - rectangles = new List(); - } + private readonly SpiralGenerator spiralGenerator = new(center); + private readonly RectangleShifter rectangleShifter = new(center); + private readonly List rectangles = new(); public Rectangle PutNextRectangle(Size rectangleSize) { diff --git a/cs/TagsCloudVisualization/SpiralGenerator.cs b/cs/TagsCloudVisualization/SpiralGenerator.cs index 590915cb8..887f151d8 100644 --- a/cs/TagsCloudVisualization/SpiralGenerator.cs +++ b/cs/TagsCloudVisualization/SpiralGenerator.cs @@ -13,7 +13,7 @@ public class SpiralGenerator public SpiralGenerator(Point center, double spiralStep = 1, double deltaPhiDegrees = (Math.PI / 180)) { if (spiralStep <= 0) - throw new ArgumentException("Spiral step must be greater than zero"); + throw new ArgumentException("Шаг спирали должен быть больше 0"); this.center = center; phi = 0; diff --git a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs index 116277219..91580b1c8 100644 --- a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs +++ b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs @@ -43,9 +43,9 @@ public void PutNextRectangle_RectanglesShouldNotOverlap() var rectangles = layouter.GetRectangles(); - for (int i = 0; i < rectangles.Count; i++) + for (var i = 0; i < rectangles.Count; i++) { - for (int j = i + 1; j < rectangles.Count; j++) + for (var j = i + 1; j < rectangles.Count; j++) { rectangles[i].IntersectsWith(rectangles[j]) .Should().BeFalse($"Прямоугольники {i} и {j} пересекаются."); diff --git a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs index 4e4465f3d..3d0245112 100644 --- a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs +++ b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs @@ -41,7 +41,7 @@ public void GetNextPoint_ShouldGenerateUniquePoints() var points = new HashSet(); const int numberOfPoints = 100; - for (int i = 0; i < numberOfPoints; i++) + for (var i = 0; i < numberOfPoints; i++) { points.Add(generator.GetNextPoint()); } @@ -56,21 +56,22 @@ public void GetNextPoint_ShouldGeneratePointsWithIncreasingDistanceFromCenter() const int numberOfPoints = 50; var initialPoint = generator.GetNextPoint(); var initialDistance = GetDistanceFromCenter(initialPoint); - var halfwayPoint = numberOfPoints / 2; - - // Генерируем половину точек - for (int i = 0; i < halfwayPoint; i++) + const int halfwayPoint = numberOfPoints / 2; + + for (var i = 0; i < halfwayPoint; i++) { generator.GetNextPoint(); } + var middlePoint = generator.GetNextPoint(); var middleDistance = GetDistanceFromCenter(middlePoint); - // Генерируем оставшиеся точки - for (int i = halfwayPoint; i < numberOfPoints; i++) + + for (var i = halfwayPoint; i < numberOfPoints; i++) { generator.GetNextPoint(); } + var finalPoint = generator.GetNextPoint(); var finalDistance = GetDistanceFromCenter(finalPoint); @@ -84,7 +85,7 @@ public void GetNextPoint_ShouldNotGenerateConsecutiveDuplicatePoints() const int numberOfPoints = 100; Point? previousPoint = null; - for (int i = 0; i < numberOfPoints; i++) + for (var i = 0; i < numberOfPoints; i++) { var currentPoint = generator.GetNextPoint(); if (previousPoint != null) From 558d45eb872e36f5ac8b9baa585d07cb2ee0a03e Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Sun, 10 Nov 2024 16:26:11 +0500 Subject: [PATCH 3/7] refactoring --- cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs index 3d0245112..394ad9a39 100644 --- a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs +++ b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs @@ -24,7 +24,7 @@ public void Constructor_WithNegativeSpiralStep_ThrowsArgumentException() Action act = () => new SpiralGenerator(center, -1); act.Should().Throw() - .WithMessage("Spiral step must be greater than zero"); + .WithMessage("Шаг спирали должен быть больше 0"); } [Test] From 24d230ca8aaf3f8ee633cdb6540b9477dae385f6 Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Thu, 14 Nov 2024 23:16:29 +0500 Subject: [PATCH 4/7] refactoring --- cs/TagsCloudVisualization/LayoutGenerator.cs | 29 +++++ cs/TagsCloudVisualization/Program.cs | 44 +------- cs/TagsCloudVisualization/RectangleShifter.cs | 99 +++++++++++------- .../TagsCloudVisualization.csproj | 4 - cs/TagsCloudVisualization/layout1.png | Bin 12702 -> 12964 bytes cs/TagsCloudVisualization/layout2.png | Bin 11764 -> 11760 bytes cs/TagsCloudVisualization/layout3.png | Bin 11843 -> 12249 bytes .../LayoutGeneratorTest.cs | 68 ++++++++++++ 8 files changed, 162 insertions(+), 82 deletions(-) create mode 100644 cs/TagsCloudVisualization/LayoutGenerator.cs create mode 100644 cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs diff --git a/cs/TagsCloudVisualization/LayoutGenerator.cs b/cs/TagsCloudVisualization/LayoutGenerator.cs new file mode 100644 index 000000000..38f3147f9 --- /dev/null +++ b/cs/TagsCloudVisualization/LayoutGenerator.cs @@ -0,0 +1,29 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public class LayoutGenerator +{ + private readonly Point center; + private readonly Size imageSize; + + public LayoutGenerator(Point center, Size imageSize) + { + this.center = center; + this.imageSize = imageSize; + } + + public void GenerateLayout(string outputFileName, int rectangleCount, Func sizeGenerator) + { + var layouter = new CircularCloudLayouter(center); + var random = new Random(); + + for (var i = 0; i < rectangleCount; i++) + { + var size = sizeGenerator(random); + layouter.PutNextRectangle(size); + } + + layouter.SaveImage(outputFileName, imageSize); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Program.cs b/cs/TagsCloudVisualization/Program.cs index 295076347..0968d5e07 100644 --- a/cs/TagsCloudVisualization/Program.cs +++ b/cs/TagsCloudVisualization/Program.cs @@ -1,43 +1,9 @@ using System.Drawing; +using TagsCloudVisualization; -GenerateLayout1(); -GenerateLayout2(); -GenerateLayout3(); -return; +var generator = new LayoutGenerator(new Point(0, 0), new Size(800, 600)); - -void GenerateLayout1() -{ - var layouter = new CircularCloudLayouter(new Point(0, 0)); - var random = new Random(); - for (var i = 0; i < 100; i++) - { - var size = new Size(random.Next(20, 100), random.Next(10, 50)); - layouter.PutNextRectangle(size); - } - layouter.SaveImage("layout1.png", new Size(800, 600)); -} - -void GenerateLayout2() -{ - var layouter = new CircularCloudLayouter(new Point(0, 0)); - for (var i = 0; i < 150; i++) - { - var size = new Size(50, 20); - layouter.PutNextRectangle(size); - } - layouter.SaveImage("layout2.png", new Size(800, 600)); -} - -void GenerateLayout3() -{ - var layouter = new CircularCloudLayouter(new Point(0, 0)); - var random = new Random(); - for (var i = 0; i < 200; i++) - { - var size = new Size(random.Next(10, 30), random.Next(10, 30)); - layouter.PutNextRectangle(size); - } - layouter.SaveImage("layout3.png", new Size(800, 600)); -} +generator.GenerateLayout("layout1.png", 100, random => new Size(random.Next(20, 100), random.Next(10, 50))); +generator.GenerateLayout("layout2.png", 150, random => new Size(50, 20)); +generator.GenerateLayout("layout3.png", 200, random => new Size(random.Next(10, 30), random.Next(10, 30))); \ No newline at end of file diff --git a/cs/TagsCloudVisualization/RectangleShifter.cs b/cs/TagsCloudVisualization/RectangleShifter.cs index dc57a2973..b07da6119 100644 --- a/cs/TagsCloudVisualization/RectangleShifter.cs +++ b/cs/TagsCloudVisualization/RectangleShifter.cs @@ -11,35 +11,41 @@ public RectangleShifter(Point center) this.center = center; } -public Rectangle ShiftRectangleToCenter(Rectangle rectangle, List rectangles) -{ - if (rectangles.Any(r => r.IntersectsWith(rectangle))) + public Rectangle ShiftRectangleToCenter(Rectangle rectangle, List rectangles) { - rectangle = FindNearestNonIntersectingPosition(rectangle, rectangles); - if (rectangle == Rectangle.Empty) - throw new InvalidOperationException("Невозможно разместить прямоугольник без пересечений."); - } + if (rectangles.Any(r => r.IntersectsWith(rectangle))) + { + rectangle = FindNearestNonIntersectingPosition(rectangle, rectangles); + if (rectangle == Rectangle.Empty) + throw new InvalidOperationException("Невозможно разместить прямоугольник без пересечений."); + } - var shiftedRectangle = rectangle; - var directionToCenter = GetDirectionToCenter(shiftedRectangle); + return MoveRectangleTowardsCenter(rectangle, rectangles); + } - while (directionToCenter != Point.Empty) + private Rectangle MoveRectangleTowardsCenter(Rectangle rectangle, List rectangles) { - var nextLocation = new Point( - shiftedRectangle.Location.X + Math.Sign(directionToCenter.X), - shiftedRectangle.Location.Y + Math.Sign(directionToCenter.Y)); + var shiftedRectangle = rectangle; + var directionToCenter = GetDirectionToCenter(shiftedRectangle); - var nextRectangle = new Rectangle(nextLocation, shiftedRectangle.Size); + while (directionToCenter != Point.Empty) + { + var nextLocation = new Point( + shiftedRectangle.Location.X + Math.Sign(directionToCenter.X), + shiftedRectangle.Location.Y + Math.Sign(directionToCenter.Y)); + + var nextRectangle = new Rectangle(nextLocation, shiftedRectangle.Size); - if (rectangles.Any(r => r.IntersectsWith(nextRectangle))) - break; + if (rectangles.Any(r => r.IntersectsWith(nextRectangle))) + break; + + shiftedRectangle = nextRectangle; + directionToCenter = GetDirectionToCenter(shiftedRectangle); + } - shiftedRectangle = nextRectangle; - directionToCenter = GetDirectionToCenter(shiftedRectangle); + return shiftedRectangle; } - return shiftedRectangle; -} private Rectangle FindNearestNonIntersectingPosition(Rectangle rectangle, List rectangles) { @@ -48,37 +54,52 @@ private Rectangle FindNearestNonIntersectingPosition(Rectangle rectangle, List 0) + { + var currentLocation = queue.Dequeue(); + var currentRectangle = new Rectangle(currentLocation, rectangle.Size); + + if (IsNonIntersecting(currentRectangle, rectangles)) + return currentRectangle; + + EnqueueAdjacentPositions(queue, visited, currentLocation, directions); + } + + return Rectangle.Empty; + } + + private Point[] GetDirections() + { + return new[] { new Point(-1, 0), new Point(1, 0), new Point(0, -1), new Point(0, 1) }; + } - while (queue.Count > 0) - { - var currentLocation = queue.Dequeue(); - var currentRectangle = new Rectangle(currentLocation, rectangle.Size); + private bool IsNonIntersecting(Rectangle currentRectangle, List rectangles) + { + return !rectangles.Any(r => r.IntersectsWith(currentRectangle)); + } - if (!rectangles.Any(r => r.IntersectsWith(currentRectangle))) - return currentRectangle; + private void EnqueueAdjacentPositions(Queue queue, HashSet visited, Point currentLocation, Point[] directions) + { + foreach (var dir in directions) + { + var nextLocation = new Point(currentLocation.X + dir.X, currentLocation.Y + dir.Y); - foreach (var dir in directions) + if (visited.Contains(nextLocation)) { - var nextLocation = new Point( - currentLocation.X + dir.X, - currentLocation.Y + dir.Y); - - if (!visited.Contains(nextLocation)) - { - queue.Enqueue(nextLocation); - visited.Add(nextLocation); - } + continue; } - } - return Rectangle.Empty; + queue.Enqueue(nextLocation); + visited.Add(nextLocation); + } } private Point GetDirectionToCenter(Rectangle rectangle) diff --git a/cs/TagsCloudVisualization/TagsCloudVisualization.csproj b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj index ec8cbc62e..250afe051 100644 --- a/cs/TagsCloudVisualization/TagsCloudVisualization.csproj +++ b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj @@ -15,8 +15,4 @@ - - - - diff --git a/cs/TagsCloudVisualization/layout1.png b/cs/TagsCloudVisualization/layout1.png index d4f866ae60a714a17c8e382ebc142cbdf826567a..14c4e058b748b937c774d873d389f0873c8e2188 100644 GIT binary patch literal 12964 zcmeHuc~}(Jwr^EYVz(kTLqrs$m_!n%-~fU^0h(?mA)s*<4H)z|AWB3)pirDmTR1=v z1&M$fJq~D`0V67HjG~Q+!GTspv4YYntw00KTf1nq(Y*Ka-FM!*_nzNUVfB3Qxu{U=3h6M*d$Vjh1 zFAO`jrDyA$Ztz>UL^5?5h8dQk|M0S?{Sg@EG{$$F*TfaGUfioYJ}Pcz)tvUAW2WQn ze()Au?mJPs)O6fSufF)|)h@Cna;CfSFWYa~rSJQNG1_2!@avnGR@e=o`VII!<{M?( z{RJ;(J!o_LHtegJQT1lWCpX@0dDRm4^PQDX+BHH9+q2b$z_9HiF@`b5)dCC)_O`$= z%x}FdhS9=k62k^rOMylEOGfZ<>c7>;rmyZKH;Km%DY9bpKwi@{!&iI#l-(aGTPW2B`wd>eK~`fdwS_H8F`zuM70L<oj1 zkqC+RLLk~>5>0;U*WLM4e&g>w@uj=Z;IBiEv@ISzkY*-aGTJ}SwNUZ4zW36yMEo`9 zsfqcLi>$IoLf4M3QswX}~-Y!SMKm3!=A;s*MFuU&fGb3k{0GR6N@ zKT6F_d^J;fcMaF{1!J)Ra}+bb+R}SYyIi~}|J?J3nDthZ)Y(?X$PhsZ4Fb`iq0-?e zI41dZw_$zds^)#Q&6gBy+~lIxQsuoOZfaCgPKJPO1He0B=(`g$&%nHx?Y8t&BPw`? z#hiIW#0-K&_;CV3E&5k%^1U?Tm$;@=w#b`0iTms2 zZFi*iJ6Qg@urfS!85p~Z*_vv(fSwmIx!ms*$ zPS1L~F)}u=E6c?K_Qjh%)UJ%AawhPSR(EO}`lJJWCGEp!$W8y8thjE@HTL{tqShAj zh7l17Mcrfzs+zpB2hw|Xg7uNI)1)Mfl)WV-C8S`YiMRt_ZWC6Sjd2-R%0D{vVoW6( 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 literal 12702 zcmeHu2~-qUw{BHYY9nGAK@mkQXapY5gSmZZn*mtGy?kHn}6N=-n#GI>sl^XSDo5t z?{Dw3&)(lTZu`>1ReRuwff$BqyYXjwVVDMmVZ^tb{_rGq&BkE(2Vdpo>VzFDF_gm( zGT`$$pJUjGom$F8{ouD|FyDI>h7G=q{^G?!zX~vHqP5%1&%a)~pth{-mc94qvXvbk z+w~?M+3xbSrCBQHkBvvRYH7U;(bUReBt=8Rirk$BEtqSz-Pmm5cAqa2f4Dui|BkN~ z8;ogvSbDwbTxQClLv88uTZjC8-nKq%xZIRtTYhqNg{*OBhe`v(3V*($gJJtOPa`nw zHrExym=Tp64ExgA5XZ0|xAHKI)?P_s*sn$jpzM_YzXzAEla)qo{5h+JD13cIXa97$ z)AiNeKdMZdpMFxXZM-gp8E)gzVWea|$-!JJIX6e)IT_0aNMs$CIu2W^{4lc*U!Nwb ze{p=1SV?jU7q29rCt%(?G5jYOY$cX>26ZHO!Lnj?J6;bIABWQc+KHiYAviSkx(g$#DC|>Sf2A7;N%{1gu<#da3gq&Y4EzUbEI`<2GYE2g@hY867`)`o?3poo<(z!lfkV zW{O&_1&?M*)Mg6|a3a)9Jo*b#{h+}`O0F_t?F_Yy1W z2JGM8Dbd`;X->GNaQ=nr+Hc_doX)2K_HB*6KXNG~vg?s6^S7s-#bEo?ew9oj$mg@w z&P&pv=m53lIXnI(D+^2VTce`#eX7FrkOR7`U1R3=D12h1E1okN?-IB$KzyyWA*w!c zMfyPQ-G*>~rOG%#Rz8z8;A$o3)eh{*2(Q`KS1Mndm2reqcE~&#Y^1*H!hm;NYE^u4 zg?Yu>HS^5q&2{E96CcMrZcS8A#-$1F8lr=ZVc9NSgGossAsR&ps9u8(R)R4Tv~^vx%-XkJjw1cY?xORCCJwpHGmg zm>$)v@N*CFx;$E=daCTr?%g|*7d%MJsL4=6Fq!9fwaH|kn>JB>Dir_TPrmN;Hsl+y z%E?+B*PNEQUNE1Ogv`~bKCz{J==kBKjt(PBbi3gCVKeq;jaMr6`aL$OnyE>3j)*q@UOK-Hz{=ApO?ORQyMd$V9$`u^pYE5B_8roDNx;}TulQ+-w z^LO#8az)nRLd?7D5w@;e*UEn%78t|&pH)DIO2O_!tYvnaP4Z{x=ZRUkHVY@j(bqIDMNtozT4|gx-=}-fD`G@4Mabxy;wje?FrX=9k*^J>1{h
MXYGOo2bzHRwkhbl9o|Mn+gjtaqQ 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 NZY~}(k2x*e_-{o92{`}& diff --git a/cs/TagsCloudVisualization/layout2.png b/cs/TagsCloudVisualization/layout2.png index bb8a183ee56517d28e5e51270002c89928b7a528..fbb9f3609327de62764f919f2372c2cbb632c2d6 100644 GIT binary patch literal 11760 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 F{|-5kU|j$J literal 11764 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 literal 11843 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 diff --git a/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs b/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs new file mode 100644 index 000000000..eb094b8d7 --- /dev/null +++ b/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs @@ -0,0 +1,68 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using TagsCloudVisualization; + +namespace TagsCloudVisualizationTest; + +[TestFixture] +public class LayoutGeneratorTests +{ + private LayoutGenerator generator; + private Point center; + private Size imageSize; + private string outputDirectory; + + [SetUp] + public void Setup() + { + center = new Point(0, 0); + imageSize = new Size(800, 600); + generator = new LayoutGenerator(center, imageSize); + outputDirectory = Path.Combine(Directory.GetCurrentDirectory(), "TestOutputs"); + + if (!Directory.Exists(outputDirectory)) + Directory.CreateDirectory(outputDirectory); + } + + [TearDown] + public void TearDown() + { + if (Directory.Exists(outputDirectory)) + Directory.Delete(outputDirectory, true); + } + + [Test] + public void GenerateLayout_ShouldCreateImageFile() + { + string outputFileName = Path.Combine(outputDirectory, "layout_test.png"); + generator.GenerateLayout(outputFileName, 10, random => new Size(50, 20)); + + File.Exists(outputFileName).Should().BeTrue("файл изображения должен быть создан"); + } + + [Test] + public void GenerateLayout_ShouldGenerateCorrectNumberOfRectangles() + { + string outputFileName = Path.Combine(outputDirectory, "layout_test.png"); + int rectangleCount = 50; + generator.GenerateLayout(outputFileName, rectangleCount, random => new Size(50, 20)); + + var layouter = new CircularCloudLayouter(center); + for (int i = 0; i < rectangleCount; i++) + { + layouter.PutNextRectangle(new Size(50, 20)); + } + + layouter.GetRectangles().Count.Should().Be(rectangleCount, "должно быть сгенерировано правильное количество прямоугольников"); + } + + [Test] + public void GenerateLayout_WithRandomSizes_ShouldCreateImageFile() + { + string outputFileName = Path.Combine(outputDirectory, "layout_test_random.png"); + generator.GenerateLayout(outputFileName, 100, random => new Size(random.Next(20, 100), random.Next(10, 50))); + + File.Exists(outputFileName).Should().BeTrue("файл изображения должен быть создан"); + } +} \ No newline at end of file From ac52e03895b2876192a8505255cc0b66832262ed Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Sun, 17 Nov 2024 19:15:25 +0500 Subject: [PATCH 5/7] refactoring --- .../CircularCloudLayouter.cs | 31 ----- .../CloudImageRenderer.cs | 45 +++++++ .../Extensions/PointExtensions.cs | 23 ++++ .../Extensions/RectangleExtensions.cs | 22 ++++ cs/TagsCloudVisualization/LayoutGenerator.cs | 7 +- .../RectangleLayouter.cs | 4 +- cs/TagsCloudVisualization/RectangleShifter.cs | 119 ------------------ .../TagsCloudVisualization.csproj | 2 - cs/TagsCloudVisualization/layout1.png | Bin 12964 -> 12910 bytes cs/TagsCloudVisualization/layout2.png | Bin 11760 -> 12067 bytes cs/TagsCloudVisualization/layout3.png | Bin 12249 -> 12349 bytes ...{Tests.cs => CircularCloudLayouterTest.cs} | 49 +++----- .../LayoutGeneratorTest.cs | 21 +--- .../RectangleLayouterTests.cs | 4 +- .../RectangleShifterTest.cs | 54 -------- .../SpiralGeneratorTests.cs | 32 +---- .../TagsCloudVisualizationTest.csproj | 1 - 17 files changed, 127 insertions(+), 287 deletions(-) create mode 100644 cs/TagsCloudVisualization/CloudImageRenderer.cs create mode 100644 cs/TagsCloudVisualization/Extensions/PointExtensions.cs create mode 100644 cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs delete mode 100644 cs/TagsCloudVisualization/RectangleShifter.cs rename cs/TagsCloudVisualizationTest/{Tests.cs => CircularCloudLayouterTest.cs} (76%) delete mode 100644 cs/TagsCloudVisualizationTest/RectangleShifterTest.cs diff --git a/cs/TagsCloudVisualization/CircularCloudLayouter.cs b/cs/TagsCloudVisualization/CircularCloudLayouter.cs index 7b8283c82..8545e159f 100644 --- a/cs/TagsCloudVisualization/CircularCloudLayouter.cs +++ b/cs/TagsCloudVisualization/CircularCloudLayouter.cs @@ -21,35 +21,4 @@ public List GetRectangles() { return rectangleLayouter.GetRectangles(); } - - public void SaveImage(string filename, Size imageSize) - { - using var bitmap = new Bitmap(imageSize.Width, imageSize.Height); - using var graphics = Graphics.FromImage(bitmap); - graphics.Clear(Color.Black); - - var random = new Random(); - foreach (var rectangle in GetRectangles()) - { - var color = Color.FromArgb( - random.Next(128, 255), - random.Next(128, 255), - random.Next(128, 255) - ); - - using var brush = new SolidBrush(Color.FromArgb(180, color)); - using var pen = new Pen(color, 2f); - - var rect = new Rectangle( - rectangle.X + imageSize.Width / 2, - rectangle.Y + imageSize.Height / 2, - rectangle.Width, - rectangle.Height); - - graphics.FillRectangle(brush, rect); - graphics.DrawRectangle(pen, rect); - } - - bitmap.Save(filename, ImageFormat.Png); - } } \ No newline at end of file diff --git a/cs/TagsCloudVisualization/CloudImageRenderer.cs b/cs/TagsCloudVisualization/CloudImageRenderer.cs new file mode 100644 index 000000000..8f54fab72 --- /dev/null +++ b/cs/TagsCloudVisualization/CloudImageRenderer.cs @@ -0,0 +1,45 @@ +using System.Drawing; +using System.Drawing.Imaging; + +namespace TagsCloudVisualization; + +public class CloudImageRenderer +{ + private readonly Size imageSize; + + public CloudImageRenderer(Size imageSize) + { + this.imageSize = imageSize; + } + + public void SaveToFile(string filename, IEnumerable rectangles) + { + using var bitmap = new Bitmap(imageSize.Width, imageSize.Height); + using var graphics = Graphics.FromImage(bitmap); + graphics.Clear(Color.Black); + + var random = new Random(); + foreach (var rectangle in rectangles) + { + var color = Color.FromArgb( + random.Next(128, 255), + random.Next(128, 255), + random.Next(128, 255) + ); + + using var brush = new SolidBrush(Color.FromArgb(180, color)); + using var pen = new Pen(color, 2f); + + var rect = new Rectangle( + rectangle.X + imageSize.Width / 2, + rectangle.Y + imageSize.Height / 2, + rectangle.Width, + rectangle.Height); + + graphics.FillRectangle(brush, rect); + graphics.DrawRectangle(pen, rect); + } + + bitmap.Save(filename, ImageFormat.Png); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Extensions/PointExtensions.cs b/cs/TagsCloudVisualization/Extensions/PointExtensions.cs new file mode 100644 index 000000000..c17ef5e65 --- /dev/null +++ b/cs/TagsCloudVisualization/Extensions/PointExtensions.cs @@ -0,0 +1,23 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Extensions; + +public static class PointExtensions +{ + public static double DistanceFromCenter(this Point point, Point center) + { + var dx = point.X - center.X; + var dy = point.Y - center.Y; + return Math.Sqrt(dx * dx + dy * dy); + } + + public static Point GetDirectionToCenter(this Point center, Rectangle rectangle) + { + var rectangleCenter = rectangle.GetCenter(); + + var directionX = center.X - rectangleCenter.X; + var directionY = center.Y - rectangleCenter.Y; + + return new Point(directionX, directionY); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs b/cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs new file mode 100644 index 000000000..d4df74085 --- /dev/null +++ b/cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs @@ -0,0 +1,22 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Extensions; + +public static class RectangleExtensions +{ + public static double DistanceToCenter(this Rectangle rectangle, Point center) + { + var rectangleCenter = rectangle.GetCenter(); + + var dx = rectangleCenter.X - center.X; + var dy = rectangleCenter.Y - center.Y; + return Math.Sqrt(dx * dx + dy * dy); + } + + public static Point GetCenter(this Rectangle rectangle) + { + return new Point( + rectangle.Left + rectangle.Width / 2, + rectangle.Top + rectangle.Height / 2); + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/LayoutGenerator.cs b/cs/TagsCloudVisualization/LayoutGenerator.cs index 38f3147f9..3c7d6c127 100644 --- a/cs/TagsCloudVisualization/LayoutGenerator.cs +++ b/cs/TagsCloudVisualization/LayoutGenerator.cs @@ -6,11 +6,13 @@ public class LayoutGenerator { private readonly Point center; private readonly Size imageSize; + private readonly CloudImageRenderer renderer; public LayoutGenerator(Point center, Size imageSize) { this.center = center; - this.imageSize = imageSize; + this.imageSize = imageSize; + renderer = new CloudImageRenderer(this.imageSize); } public void GenerateLayout(string outputFileName, int rectangleCount, Func sizeGenerator) @@ -24,6 +26,7 @@ public void GenerateLayout(string outputFileName, int rectangleCount, Func rectangles = new(); public Rectangle PutNextRectangle(Size rectangleSize) @@ -22,8 +21,7 @@ public Rectangle PutNextRectangle(Size rectangleSize) point.Y - rectangleSize.Height / 2); newRectangle = new Rectangle(location, rectangleSize); } while (rectangles.Any(r => r.IntersectsWith(newRectangle))); - - newRectangle = rectangleShifter.ShiftRectangleToCenter(newRectangle, rectangles); + rectangles.Add(newRectangle); return newRectangle; } diff --git a/cs/TagsCloudVisualization/RectangleShifter.cs b/cs/TagsCloudVisualization/RectangleShifter.cs deleted file mode 100644 index b07da6119..000000000 --- a/cs/TagsCloudVisualization/RectangleShifter.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System.Drawing; - -namespace TagsCloudVisualization; - -public class RectangleShifter -{ - private readonly Point center; - - public RectangleShifter(Point center) - { - this.center = center; - } - - public Rectangle ShiftRectangleToCenter(Rectangle rectangle, List rectangles) - { - if (rectangles.Any(r => r.IntersectsWith(rectangle))) - { - rectangle = FindNearestNonIntersectingPosition(rectangle, rectangles); - if (rectangle == Rectangle.Empty) - throw new InvalidOperationException("Невозможно разместить прямоугольник без пересечений."); - } - - return MoveRectangleTowardsCenter(rectangle, rectangles); - } - - private Rectangle MoveRectangleTowardsCenter(Rectangle rectangle, List rectangles) - { - var shiftedRectangle = rectangle; - var directionToCenter = GetDirectionToCenter(shiftedRectangle); - - while (directionToCenter != Point.Empty) - { - var nextLocation = new Point( - shiftedRectangle.Location.X + Math.Sign(directionToCenter.X), - shiftedRectangle.Location.Y + Math.Sign(directionToCenter.Y)); - - var nextRectangle = new Rectangle(nextLocation, shiftedRectangle.Size); - - if (rectangles.Any(r => r.IntersectsWith(nextRectangle))) - break; - - shiftedRectangle = nextRectangle; - directionToCenter = GetDirectionToCenter(shiftedRectangle); - } - - return shiftedRectangle; - } - - - private Rectangle FindNearestNonIntersectingPosition(Rectangle rectangle, List rectangles) - { - var queue = new Queue(); - var visited = new HashSet(); - queue.Enqueue(rectangle.Location); - visited.Add(rectangle.Location); - - var directions = GetDirections(); - - while (queue.Count > 0) - { - var currentLocation = queue.Dequeue(); - var currentRectangle = new Rectangle(currentLocation, rectangle.Size); - - if (IsNonIntersecting(currentRectangle, rectangles)) - return currentRectangle; - - EnqueueAdjacentPositions(queue, visited, currentLocation, directions); - } - - return Rectangle.Empty; - } - - private Point[] GetDirections() - { - return new[] - { - new Point(-1, 0), - new Point(1, 0), - new Point(0, -1), - new Point(0, 1) - }; - } - - private bool IsNonIntersecting(Rectangle currentRectangle, List rectangles) - { - return !rectangles.Any(r => r.IntersectsWith(currentRectangle)); - } - - private void EnqueueAdjacentPositions(Queue queue, HashSet visited, Point currentLocation, Point[] directions) - { - foreach (var dir in directions) - { - var nextLocation = new Point(currentLocation.X + dir.X, currentLocation.Y + dir.Y); - - if (visited.Contains(nextLocation)) - { - continue; - } - - queue.Enqueue(nextLocation); - visited.Add(nextLocation); - } - } - - private Point GetDirectionToCenter(Rectangle rectangle) - { - var rectangleCenter = new Point( - rectangle.Left + rectangle.Width / 2, - rectangle.Top + rectangle.Height / 2); - - var directionX = center.X - rectangleCenter.X; - var directionY = center.Y - rectangleCenter.Y; - - if (directionX == 0 && directionY == 0) - return Point.Empty; - - return new Point(directionX, directionY); - } -} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/TagsCloudVisualization.csproj b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj index 250afe051..b6da4f47e 100644 --- a/cs/TagsCloudVisualization/TagsCloudVisualization.csproj +++ b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj @@ -9,9 +9,7 @@ - - diff --git a/cs/TagsCloudVisualization/layout1.png b/cs/TagsCloudVisualization/layout1.png index 14c4e058b748b937c774d873d389f0873c8e2188..0a3dc46e8e7e5adfb5f8ac1a3dbf10f4ca447b83 100644 GIT binary patch literal 12910 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 literal 12964 zcmeHuc~}(Jwr^EYVz(kTLqrs$m_!n%-~fU^0h(?mA)s*<4H)z|AWB3)pirDmTR1=v z1&M$fJq~D`0V67HjG~Q+!GTspv4YYntw00KTf1nq(Y*Ka-FM!*_nzNUVfB3Qxu{U=3h6M*d$Vjh1 zFAO`jrDyA$Ztz>UL^5?5h8dQk|M0S?{Sg@EG{$$F*TfaGUfioYJ}Pcz)tvUAW2WQn ze()Au?mJPs)O6fSufF)|)h@Cna;CfSFWYa~rSJQNG1_2!@avnGR@e=o`VII!<{M?( z{RJ;(J!o_LHtegJQT1lWCpX@0dDRm4^PQDX+BHH9+q2b$z_9HiF@`b5)dCC)_O`$= z%x}FdhS9=k62k^rOMylEOGfZ<>c7>;rmyZKH;Km%DY9bpKwi@{!&iI#l-(aGTPW2B`wd>eK~`fdwS_H8F`zuM70L<oj1 zkqC+RLLk~>5>0;U*WLM4e&g>w@uj=Z;IBiEv@ISzkY*-aGTJ}SwNUZ4zW36yMEo`9 zsfqcLi>$IoLf4M3QswX}~-Y!SMKm3!=A;s*MFuU&fGb3k{0GR6N@ zKT6F_d^J;fcMaF{1!J)Ra}+bb+R}SYyIi~}|J?J3nDthZ)Y(?X$PhsZ4Fb`iq0-?e zI41dZw_$zds^)#Q&6gBy+~lIxQsuoOZfaCgPKJPO1He0B=(`g$&%nHx?Y8t&BPw`? z#hiIW#0-K&_;CV3E&5k%^1U?Tm$;@=w#b`0iTms2 zZFi*iJ6Qg@urfS!85p~Z*_vv(fSwmIx!ms*$ zPS1L~F)}u=E6c?K_Qjh%)UJ%AawhPSR(EO}`lJJWCGEp!$W8y8thjE@HTL{tqShAj zh7l17Mcrfzs+zpB2hw|Xg7uNI)1)Mfl)WV-C8S`YiMRt_ZWC6Sjd2-R%0D{vVoW6( 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 diff --git a/cs/TagsCloudVisualization/layout2.png b/cs/TagsCloudVisualization/layout2.png index fbb9f3609327de62764f919f2372c2cbb632c2d6..cdd85975bc24c847c69f359d6492cf6a5f09a4f4 100644 GIT binary patch literal 12067 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 F{|-5kU|j$J diff --git a/cs/TagsCloudVisualization/layout3.png b/cs/TagsCloudVisualization/layout3.png index afd8016ea44bd4860eb7e905abeec69ba69ded17..98d19b62407936a5400c39457c05bffa68763b25 100644 GIT binary patch literal 12349 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 diff --git a/cs/TagsCloudVisualizationTest/Tests.cs b/cs/TagsCloudVisualizationTest/CircularCloudLayouterTest.cs similarity index 76% rename from cs/TagsCloudVisualizationTest/Tests.cs rename to cs/TagsCloudVisualizationTest/CircularCloudLayouterTest.cs index d7451409d..c948a824f 100644 --- a/cs/TagsCloudVisualizationTest/Tests.cs +++ b/cs/TagsCloudVisualizationTest/CircularCloudLayouterTest.cs @@ -2,20 +2,23 @@ using FluentAssertions; using NUnit.Framework; using NUnit.Framework.Interfaces; +using TagsCloudVisualization; +using TagsCloudVisualization.Extensions; namespace TagsCloudVisualizationTest; -[TestFixture] public class CircularCloudLayouterTests { private CircularCloudLayouter layouter; private Point center; + private CloudImageRenderer render; [SetUp] public void SetUp() { center = new Point(0, 0); layouter = new CircularCloudLayouter(center); + render = new CloudImageRenderer(new Size(800, 600)); } [Test] @@ -56,30 +59,31 @@ public void PutNextRectangle_RectanglesShouldNotOverlap() } } } - + [Test] - public void PutNextRectangle_RectanglesShouldFormACircleShape() + public void GenerateLayout_ShouldCreateCircularCloud() { - var sizes = Enumerable.Repeat(new Size(10, 10), 100).ToList(); - - foreach (var size in sizes) + var random = new Random(); + for (var i = 0; i < 100; i++) { + var size = new Size(random.Next(10, 50), random.Next(10, 50)); layouter.PutNextRectangle(size); } var rectangles = layouter.GetRectangles(); + var distances = rectangles - .Select(r => GetDistanceToCenter(GetRectangleCenter(r), center)) + .Select(r => GetRectangleCenter(r).DistanceFromCenter(center)) .ToList(); var averageDistance = distances.Average(); var maxDistance = distances.Max(); - maxDistance.Should().BeLessOrEqualTo(averageDistance * 2, "Прямоугольники расположены не компактно."); + maxDistance.Should().BeLessOrEqualTo(averageDistance * 2, "Облако не является компактным."); } [Test] - public void GenerateLayout_ShouldCreateCircularCloud() + public void GenerateLayout_ShouldPlaceAllRectangles() { var random = new Random(); for (var i = 0; i < 100; i++) @@ -89,32 +93,16 @@ public void GenerateLayout_ShouldCreateCircularCloud() } var rectangles = layouter.GetRectangles(); - rectangles.Count.Should().Be(100); - - var distances = rectangles - .Select(r => GetDistanceToCenter(GetRectangleCenter(r), center)) - .ToList(); - - var averageDistance = distances.Average(); - var maxDistance = distances.Max(); - - maxDistance.Should().BeLessOrEqualTo(averageDistance * 2, "Облако не является компактным."); + rectangles.Count.Should().Be(100, "All rectangles should be placed successfully"); } - + private Point GetRectangleCenter(Rectangle rectangle) { return new Point( rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2); } - - private double GetDistanceToCenter(Point point, Point center) - { - var dx = point.X - center.X; - var dy = point.Y - center.Y; - return Math.Sqrt(dx * dx + dy * dy); - } - + [TearDown] public void TearDown() { @@ -129,8 +117,9 @@ public void TearDown() Directory.CreateDirectory(directory); var filePath = Path.Combine(directory, $"{testName}.png"); - - layouter.SaveImage(filePath, new Size(800, 600)); + var rectangles = layouter.GetRectangles(); + + render.SaveToFile(filePath, rectangles); Console.WriteLine($"Tag cloud visualization saved to file {filePath}"); } } \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs b/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs index eb094b8d7..8970f9309 100644 --- a/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs +++ b/cs/TagsCloudVisualizationTest/LayoutGeneratorTest.cs @@ -5,7 +5,6 @@ namespace TagsCloudVisualizationTest; -[TestFixture] public class LayoutGeneratorTests { private LayoutGenerator generator; @@ -21,8 +20,7 @@ public void Setup() generator = new LayoutGenerator(center, imageSize); outputDirectory = Path.Combine(Directory.GetCurrentDirectory(), "TestOutputs"); - if (!Directory.Exists(outputDirectory)) - Directory.CreateDirectory(outputDirectory); + Directory.CreateDirectory(outputDirectory); } [TearDown] @@ -35,7 +33,7 @@ public void TearDown() [Test] public void GenerateLayout_ShouldCreateImageFile() { - string outputFileName = Path.Combine(outputDirectory, "layout_test.png"); + var outputFileName = Path.Combine(outputDirectory, "layout_test.png"); generator.GenerateLayout(outputFileName, 10, random => new Size(50, 20)); File.Exists(outputFileName).Should().BeTrue("файл изображения должен быть создан"); @@ -44,25 +42,16 @@ public void GenerateLayout_ShouldCreateImageFile() [Test] public void GenerateLayout_ShouldGenerateCorrectNumberOfRectangles() { - string outputFileName = Path.Combine(outputDirectory, "layout_test.png"); - int rectangleCount = 50; + var outputFileName = Path.Combine(outputDirectory, "layout_test.png"); + const int rectangleCount = 50; generator.GenerateLayout(outputFileName, rectangleCount, random => new Size(50, 20)); var layouter = new CircularCloudLayouter(center); - for (int i = 0; i < rectangleCount; i++) + for (var i = 0; i < rectangleCount; i++) { layouter.PutNextRectangle(new Size(50, 20)); } layouter.GetRectangles().Count.Should().Be(rectangleCount, "должно быть сгенерировано правильное количество прямоугольников"); } - - [Test] - public void GenerateLayout_WithRandomSizes_ShouldCreateImageFile() - { - string outputFileName = Path.Combine(outputDirectory, "layout_test_random.png"); - generator.GenerateLayout(outputFileName, 100, random => new Size(random.Next(20, 100), random.Next(10, 50))); - - File.Exists(outputFileName).Should().BeTrue("файл изображения должен быть создан"); - } } \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs index 91580b1c8..8a30bd9d4 100644 --- a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs +++ b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs @@ -1,11 +1,11 @@ -using System.Drawing; +using System.Diagnostics; +using System.Drawing; using FluentAssertions; using NUnit.Framework; using TagsCloudVisualization; namespace TagsCloudVisualizationTest; -[TestFixture] public class RectangleLayouterTests { private RectangleLayouter layouter; diff --git a/cs/TagsCloudVisualizationTest/RectangleShifterTest.cs b/cs/TagsCloudVisualizationTest/RectangleShifterTest.cs deleted file mode 100644 index 29788d657..000000000 --- a/cs/TagsCloudVisualizationTest/RectangleShifterTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Drawing; -using FluentAssertions; -using NUnit.Framework; -using TagsCloudVisualization; - -namespace TagsCloudVisualizationTest; - -[TestFixture] -public class RectangleShifterTests -{ - private RectangleShifter shifter; - private Point center; - - [SetUp] - public void SetUp() - { - center = new Point(0, 0); - shifter = new RectangleShifter(center); - } - - [Test] - public void ShiftRectangleToCenter_ShouldMoveRectangleCloserToCenter() - { - var rectangle = new Rectangle(10, 10, 5, 5); - var rectangles = new List(); - - var shiftedRectangle = shifter.ShiftRectangleToCenter(rectangle, rectangles); - - GetDistanceToCenter(shiftedRectangle).Should().BeLessThan(GetDistanceToCenter(rectangle)); - } - - [Test] - public void ShiftRectangleToCenter_ShouldNotIntersectWithExistingRectangles() - { - var existingRectangle = new Rectangle(0, 0, 10, 10); - var rectangle = new Rectangle(5, 5, 10, 10); - var rectangles = new List { existingRectangle }; - - var shiftedRectangle = shifter.ShiftRectangleToCenter(rectangle, rectangles); - - shiftedRectangle.IntersectsWith(existingRectangle).Should().BeFalse(); - } - - private double GetDistanceToCenter(Rectangle rect) - { - var rectangleCenter = new Point( - rect.Left + rect.Width / 2, - rect.Top + rect.Height / 2); - - var dx = rectangleCenter.X - center.X; - var dy = rectangleCenter.Y - center.Y; - return Math.Sqrt(dx * dx + dy * dy); - } -} \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs index 394ad9a39..025d732b6 100644 --- a/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs +++ b/cs/TagsCloudVisualizationTest/SpiralGeneratorTests.cs @@ -2,10 +2,10 @@ using FluentAssertions; using System.Drawing; using TagsCloudVisualization; +using TagsCloudVisualization.Extensions; namespace TagsCloudVisualizationTest; -[TestFixture] public class SpiralGeneratorTests { private Point center; @@ -34,28 +34,13 @@ public void GetNextPoint_FirstPoint_ShouldBeAtCenter() firstPoint.Should().Be(center); } - - [Test] - public void GetNextPoint_ShouldGenerateUniquePoints() - { - var points = new HashSet(); - const int numberOfPoints = 100; - - for (var i = 0; i < numberOfPoints; i++) - { - points.Add(generator.GetNextPoint()); - } - - points.Count.Should().Be(numberOfPoints, - "все точки должны быть уникальными"); - } - + [Test] public void GetNextPoint_ShouldGeneratePointsWithIncreasingDistanceFromCenter() { const int numberOfPoints = 50; var initialPoint = generator.GetNextPoint(); - var initialDistance = GetDistanceFromCenter(initialPoint); + var initialDistance = initialPoint.DistanceFromCenter(center); const int halfwayPoint = numberOfPoints / 2; for (var i = 0; i < halfwayPoint; i++) @@ -64,7 +49,7 @@ public void GetNextPoint_ShouldGeneratePointsWithIncreasingDistanceFromCenter() } var middlePoint = generator.GetNextPoint(); - var middleDistance = GetDistanceFromCenter(middlePoint); + var middleDistance = middlePoint.DistanceFromCenter(center); for (var i = halfwayPoint; i < numberOfPoints; i++) @@ -73,7 +58,7 @@ public void GetNextPoint_ShouldGeneratePointsWithIncreasingDistanceFromCenter() } var finalPoint = generator.GetNextPoint(); - var finalDistance = GetDistanceFromCenter(finalPoint); + var finalDistance = finalPoint.DistanceFromCenter(center); finalDistance.Should().BeGreaterThan(middleDistance) .And.BeGreaterThan(initialDistance); @@ -96,11 +81,4 @@ public void GetNextPoint_ShouldNotGenerateConsecutiveDuplicatePoints() previousPoint = currentPoint; } } - - private double GetDistanceFromCenter(Point point) - { - var dx = point.X - center.X; - var dy = point.Y - center.Y; - return Math.Sqrt(dx * dx + dy * dy); - } } \ No newline at end of file diff --git a/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj b/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj index d300c3912..bdaa03f7c 100644 --- a/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj +++ b/cs/TagsCloudVisualizationTest/TagsCloudVisualizationTest.csproj @@ -13,7 +13,6 @@ - From 4d6901830670a05c4d587ab8a188b2c85bcb9248 Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Sun, 17 Nov 2024 19:20:06 +0500 Subject: [PATCH 6/7] Add RectangleLayouterTest with performance load --- .../RectangleLayouterTests.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs index 8a30bd9d4..3c3442460 100644 --- a/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs +++ b/cs/TagsCloudVisualizationTest/RectangleLayouterTests.cs @@ -52,4 +52,27 @@ public void PutNextRectangle_RectanglesShouldNotOverlap() } } } + + [Test] + public void PutNextRectangle_WithManyRectangles_ShouldCompleteInReasonableTime() + { + const int rectangleCount = 10000; + var random = new Random(42); + var stopwatch = new Stopwatch(); + var rectangles = new List(); + + stopwatch.Start(); + for (var i = 0; i < rectangleCount; i++) + { + var size = new Size( + random.Next(10, 50), + random.Next(10, 50)); + + rectangles.Add(layouter.PutNextRectangle(size)); + } + stopwatch.Stop(); + + stopwatch.Elapsed.Should().BeLessThan(TimeSpan.FromSeconds(3), + "размещение большого количества прямоугольников должно выполняться быстро"); + } } \ No newline at end of file From 2291eb9a17ae8fe22b92f32a13cd4c70fa55125b Mon Sep 17 00:00:00 2001 From: Asrom11 Date: Sun, 17 Nov 2024 20:52:50 +0500 Subject: [PATCH 7/7] improve performance of rectangle intersection check --- cs/TagsCloudVisualization/Grid.cs | 64 ++++++++++++++++++ .../RectangleLayouter.cs | 5 +- cs/TagsCloudVisualization/layout1.png | Bin 12910 -> 13435 bytes cs/TagsCloudVisualization/layout2.png | Bin 12067 -> 12067 bytes cs/TagsCloudVisualization/layout3.png | Bin 12349 -> 12197 bytes .../RectangleLayouterTests.cs | 2 +- 6 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 cs/TagsCloudVisualization/Grid.cs diff --git a/cs/TagsCloudVisualization/Grid.cs b/cs/TagsCloudVisualization/Grid.cs new file mode 100644 index 000000000..ba13b5ddc --- /dev/null +++ b/cs/TagsCloudVisualization/Grid.cs @@ -0,0 +1,64 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public class Grid +{ + private readonly int cellSize; + private readonly Dictionary> cells = new(); + + public Grid(int cellSize = 50) + { + this.cellSize = cellSize; + } + public void AddRectangle(Rectangle rectangle) + { + var cellsToUpdate = GetCellsCoveredByRectangle(rectangle); + + foreach (var cell in cellsToUpdate) + { + if (!cells.ContainsKey(cell)) + { + cells[cell] = new List(); + } + + cells[cell].Add(rectangle); + } + } + + public bool IsIntersecting(Rectangle rectangle) + { + var cellsToCheck = GetCellsCoveredByRectangle(rectangle); + + foreach (var cell in cellsToCheck) + { + if (!cells.TryGetValue(cell, out var rectanglesInCell)) + { + continue; + } + + if (rectanglesInCell.Any(r => r.IntersectsWith(rectangle))) + { + return true; + } + } + + return false; + } + + private IEnumerable GetCellsCoveredByRectangle(Rectangle rectangle) + { + var startX = rectangle.Left / cellSize; + var endX = rectangle.Right / cellSize; + var startY = rectangle.Top / cellSize; + var endY = rectangle.Bottom / cellSize; + + for (var x = startX; x <= endX; x++) + { + for (var y = startY; y <= endY; y++) + { + yield return new Point(x, y); + } + } + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/RectangleLayouter.cs b/cs/TagsCloudVisualization/RectangleLayouter.cs index ef96ada72..243d17799 100644 --- a/cs/TagsCloudVisualization/RectangleLayouter.cs +++ b/cs/TagsCloudVisualization/RectangleLayouter.cs @@ -6,7 +6,7 @@ public class RectangleLayouter(Point center) { private readonly SpiralGenerator spiralGenerator = new(center); private readonly List rectangles = new(); - + private readonly Grid grid = new(); public Rectangle PutNextRectangle(Size rectangleSize) { if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) @@ -20,8 +20,9 @@ public Rectangle PutNextRectangle(Size rectangleSize) point.X - rectangleSize.Width / 2, point.Y - rectangleSize.Height / 2); newRectangle = new Rectangle(location, rectangleSize); - } while (rectangles.Any(r => r.IntersectsWith(newRectangle))); + } while (grid.IsIntersecting(newRectangle)); + grid.AddRectangle(newRectangle); rectangles.Add(newRectangle); return newRectangle; } diff --git a/cs/TagsCloudVisualization/layout1.png b/cs/TagsCloudVisualization/layout1.png index 0a3dc46e8e7e5adfb5f8ac1a3dbf10f4ca447b83..bd667ec7fd9cc81fc51668fe3a26f33edeaa9714 100644 GIT binary patch literal 13435 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 diff --git a/cs/TagsCloudVisualization/layout2.png b/cs/TagsCloudVisualization/layout2.png index cdd85975bc24c847c69f359d6492cf6a5f09a4f4..7b81724ddffc1aa1d284ed6bfe62bd5240ea6be5 100644 GIT binary patch literal 12067 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 I(dXO$1#E@>i~s-t literal 12067 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 literal 12349 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