From 89e17f96d454bff0235bf78dcd844dfd083dec23 Mon Sep 17 00:00:00 2001 From: Ilfir Sakhabutdinov Date: Mon, 11 Nov 2024 23:56:25 +0500 Subject: [PATCH 1/6] =?UTF-8?q?Add.=20=D0=A0=D0=B5=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=BC=D0=B0?= =?UTF-8?q?=D1=88=D0=BD=D0=B5=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CircularCloudLayouter.cs | 143 ++++++++++++++++++ .../ICircularCloudLayouter.cs | 8 + .../TagsCloudVisualization.csproj | 15 ++ .../Tests/CircularCloudLayouterTestCases.cs | 38 +++++ .../Tests/CircularCloudLayouterTests.cs | 124 +++++++++++++++ cs/tdd.sln | 6 + 6 files changed, 334 insertions(+) create mode 100644 cs/TagsCloudVisualization/CircularCloudLayouter.cs create mode 100644 cs/TagsCloudVisualization/ICircularCloudLayouter.cs create mode 100644 cs/TagsCloudVisualization/TagsCloudVisualization.csproj create mode 100644 cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs create mode 100644 cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs diff --git a/cs/TagsCloudVisualization/CircularCloudLayouter.cs b/cs/TagsCloudVisualization/CircularCloudLayouter.cs new file mode 100644 index 000000000..871645df3 --- /dev/null +++ b/cs/TagsCloudVisualization/CircularCloudLayouter.cs @@ -0,0 +1,143 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public class CircularCloudLayouter : ICircularCloudLayouter +{ + private readonly IList rectangles; + private readonly Point center; + private int layer; + private double angle; + private const double fullCircleTurn = Math.PI * 2; + private const int distanceLayersDifference = 1; + private const double betweenAngleDifference = Math.PI / 36; // 15° + + public CircularCloudLayouter(Point center) + { + this.center = center; + rectangles = new List(); + layer = 0; + angle = 0; + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Width == 0 || rectangleSize.Height == 0) + { + throw new ArgumentException("Размер ширины м высоты должен быть больше 0."); + } + + var rectangle = CreateNewRectangle(rectangleSize); + rectangle = RectangleCompressions(rectangle); + rectangles.Add(rectangle); + return rectangle; + } + + private Rectangle CreateNewRectangle(Size rectangleSize) + { + var rectangleLocation = GetRectangleLocation(rectangleSize); + var rectangle = new Rectangle(rectangleLocation, rectangleSize); + while (CheckRectangleOverlaps(rectangle)) + { + UpdateAngle(); + rectangleLocation = GetRectangleLocation(rectangleSize); + rectangle = new Rectangle(rectangleLocation, rectangleSize); + } + + return rectangle; + } + + private Rectangle RectangleCompressions(Rectangle rectangle) + { + var compressionRectangle = rectangle; + compressionRectangle = Compression(compressionRectangle, + (moveRectangle) => moveRectangle.X > center.X, + (moveRectangle) => moveRectangle.X + rectangle.Width < center.X, + (moveRectangle, direction) => moveRectangle with {X = moveRectangle.X + direction}); + + compressionRectangle = Compression(compressionRectangle, + (moveRectangle) => moveRectangle.Y > center.Y, + (moveRectangle) => moveRectangle.Y + moveRectangle.Height < center.Y, + (moveRectangle, direction) => moveRectangle with {Y = moveRectangle.Y + direction}); + + return compressionRectangle; + } + + private Rectangle Compression(Rectangle rectangle, + Func checkPositiveMove, + Func checkNegativeMove, + Func doMove) + { + if (checkPositiveMove(rectangle) && checkNegativeMove(rectangle) + || !checkPositiveMove(rectangle) && !checkNegativeMove(rectangle)) + { + return rectangle; + } + + var direction = checkPositiveMove(rectangle) ? -1 : 1; + var moveRectangle = rectangle; + while (true) + { + moveRectangle = doMove(moveRectangle, direction); + if (CheckRectangleOverlaps(moveRectangle)) + { + moveRectangle = doMove(moveRectangle, -1 * direction); + break; + } + + if ((direction == -1 && !checkPositiveMove(moveRectangle)) + || (direction == 1 && !checkNegativeMove(moveRectangle))) + { + break; + } + } + + return moveRectangle; + } + + private bool CheckRectangleOverlaps(Rectangle rectangle) + { + return rectangles.Any(r => r.IntersectsWith(rectangle)); + } + + private Point GetRectangleLocation(Size rectangleSize) + { + var shiftFromCenter = -1 * rectangleSize / 2; + if (!rectangles.Any()) + { + UpdateLayer(); + return center + shiftFromCenter; + } + + return CalculateLocationInSpiral() + shiftFromCenter; + } + + private Point CalculateLocationInSpiral() + { + if (!rectangles.Any()) + { + throw new ArgumentException( + "Должен быть хотя бы один прямоугольник для вычисления позиции для следующего вне центра спирали."); + } + + var x = center.X + layer * distanceLayersDifference * Math.Cos(angle); + var y = center.Y + layer * distanceLayersDifference * Math.Sin(angle); + var newLocation = new Point((int) x, (int) y); + return newLocation; + } + + private void UpdateAngle() + { + angle += betweenAngleDifference; + if (angle > fullCircleTurn) + { + UpdateLayer(); + angle = 0; + } + } + + private void UpdateLayer() + { + layer++; + } +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/ICircularCloudLayouter.cs b/cs/TagsCloudVisualization/ICircularCloudLayouter.cs new file mode 100644 index 000000000..cd5e7f9ae --- /dev/null +++ b/cs/TagsCloudVisualization/ICircularCloudLayouter.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloudVisualization; + +public interface ICircularCloudLayouter +{ + public Rectangle PutNextRectangle(Size rectangleSize); +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/TagsCloudVisualization.csproj b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj new file mode 100644 index 000000000..d50517303 --- /dev/null +++ b/cs/TagsCloudVisualization/TagsCloudVisualization.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + + + + + + + + + diff --git a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs new file mode 100644 index 000000000..703347f39 --- /dev/null +++ b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs @@ -0,0 +1,38 @@ +using System.Drawing; +using NUnit.Framework; + +namespace TagsCloudVisualization.Tests; + +internal static class CircularCloudLayouterTestCases +{ + internal static readonly IEnumerable GetRectanglesWithZeroSizesTestData = + [ + new TestCaseData(new Point(0, 0), new Size(0, 0)) + .SetName("AllZero"), + new TestCaseData(new Point(0, 0), new Size(1, 0)) + .SetName("WidthZero"), + new TestCaseData(new Point(0, 0), new Size(0, 1)) + .SetName("HeightZero"), + ]; + + internal static readonly IEnumerable GetCorrectRectangleSizesWithEndsLocationTestData = + [ + new TestCaseData(new Point(10, 10), new List() {new(10, 10)}, new Point(10, 10) - new Size(10, 10) / 2) + .SetName("FirstRectangleInCenter"), + ]; + + internal static readonly IEnumerable GetCorrectUnusualRectanglesSizeTestData = + [ + new TestCaseData(new Point(10, 10), Array.Empty()) + .SetName("ArraySizeEmpty"), + ]; + + internal static readonly IEnumerable GetCorrectRectangleSizesTestData = + [ + new TestCaseData(new Point(10, 10), new List() {new(1, 1)}) + .SetName("OneSize"), + new TestCaseData(new Point(100, 100), new List() + {new(10, 10), new(20, 20), new(15, 15), new(5, 7), new(3, 1), new(15, 35)}) + .SetName("MoreSizes"), + ]; +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs new file mode 100644 index 000000000..4122bd34c --- /dev/null +++ b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs @@ -0,0 +1,124 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; + +namespace TagsCloudVisualization.Tests; + +[TestFixture] +public class CircularCloudLayouterTests +{ + private static IEnumerable withZeroSizeTestCases + = CircularCloudLayouterTestCases.GetRectanglesWithZeroSizesTestData; + + private static IEnumerable checkRectangleLocationTestCases + = CircularCloudLayouterTestCases.GetCorrectRectangleSizesWithEndsLocationTestData; + + private static IEnumerable checkRectangleSizeTestCases + = CircularCloudLayouterTestCases.GetCorrectRectangleSizesTestData; + + private static IEnumerable checkUnusualRectangleSizeTestCases + = CircularCloudLayouterTestCases.GetCorrectUnusualRectanglesSizeTestData; + + [Test] + [TestCaseSource(nameof(withZeroSizeTestCases))] + public void PutNextRectangle_ShouldThrowArgumentException_WhenSizeIsZero(Point center, Size rectangleSize) + { + var circularCloudLayouter = new CircularCloudLayouter(center); + + var action = () => circularCloudLayouter.PutNextRectangle(rectangleSize); + + action.Should() + .Throw() + .WithMessage("Размер ширины м высоты должен быть больше 0."); + } + + [Test] + [TestCaseSource(nameof(checkRectangleLocationTestCases))] + public void PutNextRectangle_ShouldLastRectanglesInLocation_WhenBeforePutNextRectangles(Point center, + IList sizes, Point shouldLocation) + { + if (!sizes.Any()) + { + throw new ArgumentNullException(nameof(sizes)); + } + + var circularCloudLayouter = new CircularCloudLayouter(center); + + var rectangles = sizes.Select(size => circularCloudLayouter.PutNextRectangle(size)).ToList(); + var rectangle = rectangles.Last(); + + rectangle.Location.Should() + .BeEquivalentTo(shouldLocation); + } + + [Test] + [TestCaseSource(nameof(checkRectangleSizeTestCases))] + public void PutNextRectangle_ShouldFirstRectanglesInCenter_WhenAfterPutNextRectangles(Point center, + IList sizes) + { + if (!sizes.Any()) + { + throw new ArgumentNullException(nameof(sizes)); + } + + var circularCloudLayouter = new CircularCloudLayouter(center); + var firstRectangleSize = sizes.First(); + var firstRectangle = circularCloudLayouter.PutNextRectangle(firstRectangleSize); + var shiftFromCenter = -1 * firstRectangleSize / 2; + var shouldRectangleLocation = center + shiftFromCenter; + + var _ = sizes.Skip(1).Select(size => circularCloudLayouter.PutNextRectangle(size)); + + firstRectangle.Location.Should() + .BeEquivalentTo(shouldRectangleLocation); + } + + [Test] + [TestCaseSource(nameof(checkRectangleSizeTestCases))] + [TestCaseSource(nameof(checkUnusualRectangleSizeTestCases))] + public void PutNextRectangle_ShouldNotInCenter_WhenPutNextRectangles(Point center, IList sizes) + { + var rectangles = new List(); + var circularCloudLayouter = new CircularCloudLayouter(center); + + if (sizes.Any()) + { + var _ = circularCloudLayouter.PutNextRectangle(sizes.First()); + rectangles.AddRange(sizes.Skip(1).Select(size => circularCloudLayouter.PutNextRectangle(size))); + } + + rectangles.Should().NotContain(r => + r.Contains(center) + ); + } + + [Test] + [TestCaseSource(nameof(checkRectangleSizeTestCases))] + [TestCaseSource(nameof(checkUnusualRectangleSizeTestCases))] + public void PutNextRectangle_ShouldEquivalentSize_WhenPutNextRectangles(Point center, IList sizes) + { + var circularCloudLayouter = new CircularCloudLayouter(center); + var rectangles = sizes.Select(size => circularCloudLayouter.PutNextRectangle(size)).ToList(); + + rectangles.Select(r => r.Size).Should() + .BeEquivalentTo(sizes); + } + + [Test] + [TestCaseSource(nameof(checkRectangleSizeTestCases))] + [TestCaseSource(nameof(checkUnusualRectangleSizeTestCases))] + public void PutNextRectangle_NotShouldIntersect_WhenPutNextRectangles(Point center, IList sizes) + { + var circularCloudLayouter = new CircularCloudLayouter(center); + var rectangles = sizes.Select(size => circularCloudLayouter.PutNextRectangle(size)).ToList(); + var intersectingRectangles = new List(); + + foreach (var checkRectangle in rectangles) + { + intersectingRectangles.AddRange(rectangles + .Where(rectangle => checkRectangle != rectangle && rectangle.IntersectsWith(checkRectangle))); + } + + intersectingRectangles.Should().BeEmpty(); + } +} \ No newline at end of file diff --git a/cs/tdd.sln b/cs/tdd.sln index c8f523d63..24965180c 100644 --- a/cs/tdd.sln +++ b/cs/tdd.sln @@ -7,6 +7,8 @@ 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", "{4115D7AB-9182-4A6D-AC0F-9C209291B549}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ 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 + {4115D7AB-9182-4A6D-AC0F-9C209291B549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4115D7AB-9182-4A6D-AC0F-9C209291B549}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4115D7AB-9182-4A6D-AC0F-9C209291B549}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4115D7AB-9182-4A6D-AC0F-9C209291B549}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 0638a567e14519068e18bbf4428560e7f6b49209 Mon Sep 17 00:00:00 2001 From: Ilfir Sakhabutdinov Date: Tue, 12 Nov 2024 10:52:08 +0500 Subject: [PATCH 2/6] =?UTF-8?q?Update.=20=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D1=88=D0=B8=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE?= =?UTF-8?q?=D0=BC=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B8=20=D1=83?= =?UTF-8?q?=D0=BB=D1=83=D1=87=D1=88=D0=B0=D1=8E=D1=89=D0=B8=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cs/TagsCloudVisualization/CircularCloudLayouter.cs | 4 +++- .../Extensions/RectangleExtensions.cs | 12 ++++++++++++ .../Tests/CircularCloudLayouterTestCases.cs | 4 ++-- .../Tests/CircularCloudLayouterTests.cs | 11 +++++------ 4 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs diff --git a/cs/TagsCloudVisualization/CircularCloudLayouter.cs b/cs/TagsCloudVisualization/CircularCloudLayouter.cs index 871645df3..fe989fcae 100644 --- a/cs/TagsCloudVisualization/CircularCloudLayouter.cs +++ b/cs/TagsCloudVisualization/CircularCloudLayouter.cs @@ -122,7 +122,9 @@ private Point CalculateLocationInSpiral() var x = center.X + layer * distanceLayersDifference * Math.Cos(angle); var y = center.Y + layer * distanceLayersDifference * Math.Sin(angle); - var newLocation = new Point((int) x, (int) y); + var wholeX = Convert.ToInt32(x); + var wholeY = Convert.ToInt32(y); + var newLocation = new Point(wholeX, wholeY); return newLocation; } diff --git a/cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs b/cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs new file mode 100644 index 000000000..7eac9a48c --- /dev/null +++ b/cs/TagsCloudVisualization/Extensions/RectangleExtensions.cs @@ -0,0 +1,12 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Extensions; + +public static class RectangleExtensions +{ + public static Point Center(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/Tests/CircularCloudLayouterTestCases.cs b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs index 703347f39..e97eb3b53 100644 --- a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs +++ b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs @@ -31,8 +31,8 @@ internal static class CircularCloudLayouterTestCases [ new TestCaseData(new Point(10, 10), new List() {new(1, 1)}) .SetName("OneSize"), - new TestCaseData(new Point(100, 100), new List() - {new(10, 10), new(20, 20), new(15, 15), new(5, 7), new(3, 1), new(15, 35)}) + new TestCaseData(new Point(100, 100), new List() + {new(10, 10), new(20, 20), new(15, 15), new(5, 7), new(3, 1), new(15, 35)}) .SetName("MoreSizes"), ]; } \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs index 4122bd34c..2bb32d55f 100644 --- a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs +++ b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs @@ -1,6 +1,7 @@ using System.Drawing; using FluentAssertions; using NUnit.Framework; +using TagsCloudVisualization.Extensions; namespace TagsCloudVisualization.Tests; @@ -63,20 +64,18 @@ public void PutNextRectangle_ShouldFirstRectanglesInCenter_WhenAfterPutNextRecta var circularCloudLayouter = new CircularCloudLayouter(center); var firstRectangleSize = sizes.First(); - var firstRectangle = circularCloudLayouter.PutNextRectangle(firstRectangleSize); - var shiftFromCenter = -1 * firstRectangleSize / 2; - var shouldRectangleLocation = center + shiftFromCenter; + var firstRectangle = circularCloudLayouter.PutNextRectangle(firstRectangleSize); var _ = sizes.Skip(1).Select(size => circularCloudLayouter.PutNextRectangle(size)); - firstRectangle.Location.Should() - .BeEquivalentTo(shouldRectangleLocation); + firstRectangle.Center().Should() + .BeEquivalentTo(center); } [Test] [TestCaseSource(nameof(checkRectangleSizeTestCases))] [TestCaseSource(nameof(checkUnusualRectangleSizeTestCases))] - public void PutNextRectangle_ShouldNotInCenter_WhenPutNextRectangles(Point center, IList sizes) + public void PutNextRectangle_ShouldRectanglesNotInCenter_WhenPutNextRectangles(Point center, IList sizes) { var rectangles = new List(); var circularCloudLayouter = new CircularCloudLayouter(center); From 5a912cc9e62bebbaf98e0908723b72df8ec379a8 Mon Sep 17 00:00:00 2001 From: Ilfir Sakhabutdinov Date: Tue, 12 Nov 2024 11:10:05 +0500 Subject: [PATCH 3/6] =?UTF-8?q?Add.=20=D0=A0=D0=B5=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=B3=D0=BE=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=BC=D0=B0?= =?UTF-8?q?=D1=88=D0=BD=D0=B5=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ITagsCloudVisualizer.cs | 10 ++ .../Images/100RectanglesFrom50To100Sizes.png | Bin 0 -> 38827 bytes .../Images/100RectanglesFrom50To50Sizes.png | Bin 0 -> 18898 bytes .../Images/100RectanglesFrom75To100Sizes.png | Bin 0 -> 42541 bytes ...idthFrom50To75AndHeightFrom25To50Sizes.png | Bin 0 -> 24309 bytes cs/TagsCloudVisualization/README.md | 5 + .../TagsCloudVisualization.csproj | 5 + .../TagsCloudVisualizer.cs | 86 ++++++++++++++++++ 8 files changed, 106 insertions(+) create mode 100644 cs/TagsCloudVisualization/ITagsCloudVisualizer.cs create mode 100644 cs/TagsCloudVisualization/Images/100RectanglesFrom50To100Sizes.png create mode 100644 cs/TagsCloudVisualization/Images/100RectanglesFrom50To50Sizes.png create mode 100644 cs/TagsCloudVisualization/Images/100RectanglesFrom75To100Sizes.png create mode 100644 cs/TagsCloudVisualization/Images/100RectanglesWithWidthFrom50To75AndHeightFrom25To50Sizes.png create mode 100644 cs/TagsCloudVisualization/README.md create mode 100644 cs/TagsCloudVisualization/TagsCloudVisualizer.cs diff --git a/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs b/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs new file mode 100644 index 000000000..45e0ef0ab --- /dev/null +++ b/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs @@ -0,0 +1,10 @@ +using System.Drawing; +using System.Drawing.Imaging; + +namespace TagsCloudVisualization; + +public interface ITagsCloudVisualizer +{ + public void VisualizeLayoutRectangles(IList rectangles); + public void Save(string outputFilePath, ImageFormat imageFormat); +} \ No newline at end of file diff --git a/cs/TagsCloudVisualization/Images/100RectanglesFrom50To100Sizes.png b/cs/TagsCloudVisualization/Images/100RectanglesFrom50To100Sizes.png new file mode 100644 index 0000000000000000000000000000000000000000..f3965a7c3abae5b1be04150592ac757f8992e9fd GIT binary patch literal 38827 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 literal 0 HcmV?d00001 diff --git a/cs/TagsCloudVisualization/Images/100RectanglesFrom50To50Sizes.png b/cs/TagsCloudVisualization/Images/100RectanglesFrom50To50Sizes.png new file mode 100644 index 0000000000000000000000000000000000000000..e95ef99046f71b4b34d3396dfa163ae2683507a9 GIT binary patch literal 18898 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 literal 0 HcmV?d00001 diff --git a/cs/TagsCloudVisualization/Images/100RectanglesFrom75To100Sizes.png b/cs/TagsCloudVisualization/Images/100RectanglesFrom75To100Sizes.png new file mode 100644 index 0000000000000000000000000000000000000000..e49c48b3c4149e141d1656ff67a64dd68046075c GIT binary patch literal 42541 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 I_0U)U4*?hY-~a#s literal 0 HcmV?d00001 diff --git a/cs/TagsCloudVisualization/Images/100RectanglesWithWidthFrom50To75AndHeightFrom25To50Sizes.png b/cs/TagsCloudVisualization/Images/100RectanglesWithWidthFrom50To75AndHeightFrom25To50Sizes.png new file mode 100644 index 0000000000000000000000000000000000000000..96995c0e1109616d94e719dbf5c88088a296f290 GIT binary patch literal 24309 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 + + + + + diff --git a/cs/TagsCloudVisualization/TagsCloudVisualizer.cs b/cs/TagsCloudVisualization/TagsCloudVisualizer.cs new file mode 100644 index 000000000..effa58352 --- /dev/null +++ b/cs/TagsCloudVisualization/TagsCloudVisualizer.cs @@ -0,0 +1,86 @@ +using System.Drawing; +using System.Drawing.Imaging; +using TagsCloudVisualization.Extensions; + +namespace TagsCloudVisualization; + +public class TagsCloudVisualizer : ITagsCloudVisualizer, IDisposable +{ + private bool isDisposed; + private readonly Bitmap bitmap; + + public TagsCloudVisualizer(Size imageSize) + { + bitmap = new Bitmap(imageSize.Width, imageSize.Height); + isDisposed = false; + } + + public void VisualizeLayoutRectangles(IList rectangles) + { + using var graphics = Graphics.FromImage(bitmap); + VisualizeRectangles(graphics, rectangles); + VisualizeCenter(graphics, rectangles.First()); + } + + public void Save(string outputFilePath, ImageFormat imageFormat) + { + bitmap.Save(outputFilePath, imageFormat); + } + + private void VisualizeRectangles(Graphics graphics, IEnumerable rectangles) + { + foreach (var rectangle in rectangles) + { + VisualizeRectangle(graphics, rectangle); + } + } + + private void VisualizeRectangle(Graphics graphics, Rectangle rectangle) + { + graphics.FillRectangle(Brushes.Blue, rectangle); + graphics.DrawRectangle(Pens.Black, rectangle); + } + + private void VisualizeCenter(Graphics graphics, Rectangle firstRectangle) + { + var center = firstRectangle.Center(); + var centerRectangle = CalculateCentralRectangle(center); + VisualizeCenterPoint(graphics, centerRectangle); + } + + private Rectangle CalculateCentralRectangle(Point center) + { + var size = new Size(2, 2); + return new Rectangle(center.X - size.Width / 2, center.Y - size.Height / 2, size.Width, size.Height); + } + + private void VisualizeCenterPoint(Graphics graphics, Rectangle centerRectangle) + { + graphics.FillEllipse(Brushes.Red, centerRectangle); + } + + ~TagsCloudVisualizer() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool fromDisposeMethod) + { + if (isDisposed) + { + return; + } + + if (fromDisposeMethod) + { + bitmap.Dispose(); + } + + isDisposed = true; + } +} \ No newline at end of file From d67e9101691ea87baa4cd305c3575ed428ed16f8 Mon Sep 17 00:00:00 2001 From: Ilfir Sakhabutdinov Date: Tue, 12 Nov 2024 22:45:55 +0500 Subject: [PATCH 4/6] =?UTF-8?q?Update.=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B4=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D1=80=D1=8F=D0=BC?= =?UTF-8?q?=D0=BE=D1=83=D0=B3=D0=BE=D0=BB=D1=8C=D0=BD=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B8=D0=B7=20Tag=20Cloud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cs/TagsCloudVisualization/CircularCloudLayouter.cs | 5 +++++ cs/TagsCloudVisualization/ICircularCloudLayouter.cs | 1 + 2 files changed, 6 insertions(+) diff --git a/cs/TagsCloudVisualization/CircularCloudLayouter.cs b/cs/TagsCloudVisualization/CircularCloudLayouter.cs index fe989fcae..be1d39e12 100644 --- a/cs/TagsCloudVisualization/CircularCloudLayouter.cs +++ b/cs/TagsCloudVisualization/CircularCloudLayouter.cs @@ -33,6 +33,11 @@ public Rectangle PutNextRectangle(Size rectangleSize) return rectangle; } + public IReadOnlyCollection GetRectangles() + { + return rectangles.AsReadOnly(); + } + private Rectangle CreateNewRectangle(Size rectangleSize) { var rectangleLocation = GetRectangleLocation(rectangleSize); diff --git a/cs/TagsCloudVisualization/ICircularCloudLayouter.cs b/cs/TagsCloudVisualization/ICircularCloudLayouter.cs index cd5e7f9ae..0804b3f1d 100644 --- a/cs/TagsCloudVisualization/ICircularCloudLayouter.cs +++ b/cs/TagsCloudVisualization/ICircularCloudLayouter.cs @@ -5,4 +5,5 @@ namespace TagsCloudVisualization; public interface ICircularCloudLayouter { public Rectangle PutNextRectangle(Size rectangleSize); + public IReadOnlyCollection GetRectangles(); } \ No newline at end of file From 69d5061a0bc1d17cd374d049abcd81753233c633 Mon Sep 17 00:00:00 2001 From: Ilfir Sakhabutdinov Date: Tue, 12 Nov 2024 22:49:26 +0500 Subject: [PATCH 5/6] =?UTF-8?q?Refactor.=20=D0=94=D0=B0=D0=BB=20=D0=B1?= =?UTF-8?q?=D0=BE=D0=BB=D0=B5=D0=B5=20=D0=BE=D1=81=D0=BC=D1=8B=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D0=BE=D0=B5=20=D0=B8=D0=BC=D1=8F=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B4=D1=83=20=D0=B8=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=81=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84?= =?UTF-8?q?=D0=B5=D0=B9=D1=81=20IDisposable.=20=D0=A2=D0=B5=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D1=8C=20ITagsCloudVisualizer=20=D0=BD=D0=B0=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=D0=B4=D1=83=D0=B5=D1=82=20IDisposable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cs/TagsCloudVisualization/ITagsCloudVisualizer.cs | 4 ++-- cs/TagsCloudVisualization/TagsCloudVisualizer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs b/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs index 45e0ef0ab..f96e0fcb2 100644 --- a/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs +++ b/cs/TagsCloudVisualization/ITagsCloudVisualizer.cs @@ -3,8 +3,8 @@ namespace TagsCloudVisualization; -public interface ITagsCloudVisualizer +public interface ITagsCloudVisualizer : IDisposable { - public void VisualizeLayoutRectangles(IList rectangles); + public void AddVisualizationRectangles(IEnumerable rectangles); public void Save(string outputFilePath, ImageFormat imageFormat); } \ No newline at end of file diff --git a/cs/TagsCloudVisualization/TagsCloudVisualizer.cs b/cs/TagsCloudVisualization/TagsCloudVisualizer.cs index effa58352..2c047e029 100644 --- a/cs/TagsCloudVisualization/TagsCloudVisualizer.cs +++ b/cs/TagsCloudVisualization/TagsCloudVisualizer.cs @@ -4,7 +4,7 @@ namespace TagsCloudVisualization; -public class TagsCloudVisualizer : ITagsCloudVisualizer, IDisposable +public class TagsCloudVisualizer : ITagsCloudVisualizer { private bool isDisposed; private readonly Bitmap bitmap; @@ -15,7 +15,7 @@ public TagsCloudVisualizer(Size imageSize) isDisposed = false; } - public void VisualizeLayoutRectangles(IList rectangles) + public void AddVisualizationRectangles(IEnumerable rectangles) { using var graphics = Graphics.FromImage(bitmap); VisualizeRectangles(graphics, rectangles); From 68c7d00f5bb23e9190672346b7de8d3f263fc73b Mon Sep 17 00:00:00 2001 From: Ilfir Sakhabutdinov Date: Tue, 12 Nov 2024 23:02:23 +0500 Subject: [PATCH 6/6] =?UTF-8?q?Add.=20=D0=A0=D0=B5=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=82=D1=80=D0=B5=D1=82=D1=8C=D0=B5=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D1=88=D0=BD=D0=B5=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tests/CircularCloudLayouterTestCases.cs | 4 +- .../Tests/CircularCloudLayouterTests.cs | 47 ++++++++++++++++--- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs index e97eb3b53..9a659f989 100644 --- a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs +++ b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTestCases.cs @@ -23,13 +23,13 @@ internal static class CircularCloudLayouterTestCases internal static readonly IEnumerable GetCorrectUnusualRectanglesSizeTestData = [ - new TestCaseData(new Point(10, 10), Array.Empty()) + new TestCaseData(new Point(100, 100), Array.Empty()) .SetName("ArraySizeEmpty"), ]; internal static readonly IEnumerable GetCorrectRectangleSizesTestData = [ - new TestCaseData(new Point(10, 10), new List() {new(1, 1)}) + new TestCaseData(new Point(100, 100), new List() {new(10, 10)}) .SetName("OneSize"), new TestCaseData(new Point(100, 100), new List() {new(10, 10), new(20, 20), new(15, 15), new(5, 7), new(3, 1), new(15, 35)}) diff --git a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs index 2bb32d55f..74b131ca0 100644 --- a/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs +++ b/cs/TagsCloudVisualization/Tests/CircularCloudLayouterTests.cs @@ -1,6 +1,8 @@ using System.Drawing; +using System.Drawing.Imaging; using FluentAssertions; using NUnit.Framework; +using NUnit.Framework.Interfaces; using TagsCloudVisualization.Extensions; namespace TagsCloudVisualization.Tests; @@ -8,6 +10,8 @@ namespace TagsCloudVisualization.Tests; [TestFixture] public class CircularCloudLayouterTests { + private ICircularCloudLayouter circularCloudLayouter; + private static IEnumerable withZeroSizeTestCases = CircularCloudLayouterTestCases.GetRectanglesWithZeroSizesTestData; @@ -20,11 +24,23 @@ private static IEnumerable checkRectangleSizeTestCases private static IEnumerable checkUnusualRectangleSizeTestCases = CircularCloudLayouterTestCases.GetCorrectUnusualRectanglesSizeTestData; + [TearDown] + public void TearDown() + { + if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed) + return; + var rectangles = circularCloudLayouter.GetRectangles(); + var imageSize = new Size(1000, 1000); + using var tagsCloudVisualizer = new TagsCloudVisualizer(imageSize); + tagsCloudVisualizer.AddVisualizationRectangles(rectangles); + SaveErrorVisualization(tagsCloudVisualizer, TestContext.CurrentContext); + } + [Test] [TestCaseSource(nameof(withZeroSizeTestCases))] public void PutNextRectangle_ShouldThrowArgumentException_WhenSizeIsZero(Point center, Size rectangleSize) { - var circularCloudLayouter = new CircularCloudLayouter(center); + circularCloudLayouter = new CircularCloudLayouter(center); var action = () => circularCloudLayouter.PutNextRectangle(rectangleSize); @@ -43,7 +59,7 @@ public void PutNextRectangle_ShouldLastRectanglesInLocation_WhenBeforePutNextRec throw new ArgumentNullException(nameof(sizes)); } - var circularCloudLayouter = new CircularCloudLayouter(center); + circularCloudLayouter = new CircularCloudLayouter(center); var rectangles = sizes.Select(size => circularCloudLayouter.PutNextRectangle(size)).ToList(); var rectangle = rectangles.Last(); @@ -62,7 +78,7 @@ public void PutNextRectangle_ShouldFirstRectanglesInCenter_WhenAfterPutNextRecta throw new ArgumentNullException(nameof(sizes)); } - var circularCloudLayouter = new CircularCloudLayouter(center); + circularCloudLayouter = new CircularCloudLayouter(center); var firstRectangleSize = sizes.First(); var firstRectangle = circularCloudLayouter.PutNextRectangle(firstRectangleSize); @@ -78,7 +94,7 @@ public void PutNextRectangle_ShouldFirstRectanglesInCenter_WhenAfterPutNextRecta public void PutNextRectangle_ShouldRectanglesNotInCenter_WhenPutNextRectangles(Point center, IList sizes) { var rectangles = new List(); - var circularCloudLayouter = new CircularCloudLayouter(center); + circularCloudLayouter = new CircularCloudLayouter(center); if (sizes.Any()) { @@ -96,7 +112,7 @@ public void PutNextRectangle_ShouldRectanglesNotInCenter_WhenPutNextRectangles(P [TestCaseSource(nameof(checkUnusualRectangleSizeTestCases))] public void PutNextRectangle_ShouldEquivalentSize_WhenPutNextRectangles(Point center, IList sizes) { - var circularCloudLayouter = new CircularCloudLayouter(center); + circularCloudLayouter = new CircularCloudLayouter(center); var rectangles = sizes.Select(size => circularCloudLayouter.PutNextRectangle(size)).ToList(); rectangles.Select(r => r.Size).Should() @@ -108,7 +124,7 @@ public void PutNextRectangle_ShouldEquivalentSize_WhenPutNextRectangles(Point ce [TestCaseSource(nameof(checkUnusualRectangleSizeTestCases))] public void PutNextRectangle_NotShouldIntersect_WhenPutNextRectangles(Point center, IList sizes) { - var circularCloudLayouter = new CircularCloudLayouter(center); + circularCloudLayouter = new CircularCloudLayouter(center); var rectangles = sizes.Select(size => circularCloudLayouter.PutNextRectangle(size)).ToList(); var intersectingRectangles = new List(); @@ -120,4 +136,23 @@ public void PutNextRectangle_NotShouldIntersect_WhenPutNextRectangles(Point cent intersectingRectangles.Should().BeEmpty(); } + + private void SaveErrorVisualization(ITagsCloudVisualizer tagsCloudVisualizer, TestContext testContext) + { + var imageFormat = ImageFormat.Png; + var imagePath = GetTestErrorImagesPath(testContext, imageFormat); + tagsCloudVisualizer.Save(imagePath, imageFormat); + TestContext.WriteLine($"Tag cloud visualization saved to file {imagePath}"); + } + + private string GetTestErrorImagesPath(TestContext testContext, ImageFormat imageFormat) + { + const string imagesFolderName = "ImagesWhenErrorInTests"; + var path = testContext.TestDirectory; + var imagesDirectoryPath = Path.Combine(path, imagesFolderName); + var testName = testContext.Test.Name; + Directory.CreateDirectory(imagesDirectoryPath); + var fileName = Path.ChangeExtension(testName, imageFormat.ToString().ToLowerInvariant()); + return Path.Combine(imagesDirectoryPath, fileName); + } } \ No newline at end of file