From 6a2688df92c460e08d06d87f01658c90285491ba Mon Sep 17 00:00:00 2001 From: Zakhar Date: Thu, 19 Dec 2024 05:00:35 +0500 Subject: [PATCH 01/17] =?UTF-8?q?=D0=A7=D0=B5=D1=80=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20?= =?UTF-8?q?=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...rcularCloudLayouterMainRequirementsTest.cs | 177 ++++++++++++++++++ .../CircularCloudLayouterTest.cs | 22 +++ .../RandomCloudLayouterWorkerTest.cs | 30 +++ .../Extensions/RectangleExtensions.cs | 16 ++ .../ImageSaversTests/ImageSaverTest.cs | 61 ++++++ TagCloud.Tests/TagCloud.Tests.csproj | 34 ++++ .../WordCountersTests/WordCounterTest.cs | 46 +++++ .../WordReadersTests/WordReaderTest.cs | 73 ++++++++ .../CloudLayouterPainter.cs | 57 ++++++ .../ICloudLayouterPainter.cs | 10 + .../FrequencyBasedCloudLayouterWorker.cs | 17 ++ .../ICloudLayouterWorker.cs | 10 + .../RandomCloudLayouterWorker.cs | 51 +++++ .../CircularCloudLayouter/Circle.cs | 24 +++ .../CircularCloudLayouter.cs | 68 +++++++ TagCloud/CloudLayouters/ICloudLayouter.cs | 10 + TagCloud/ImageSavers/IImageSaver.cs | 10 + TagCloud/ImageSavers/ImageSaver.cs | 22 +++ TagCloud/Normalizers/INormalizer.cs | 8 + TagCloud/Normalizers/Normalizer.cs | 10 + TagCloud/Program.cs | 106 +++++++++++ TagCloud/Tag.cs | 8 + TagCloud/TagCloud.csproj | 14 ++ TagCloud/WordCounters/IWordCounter.cs | 9 + TagCloud/WordCounters/WordCounter.cs | 18 ++ TagCloud/WordFilters/IWordFilter.cs | 8 + TagCloud/WordFilters/WordFilter.cs | 10 + TagCloud/WordReaders/IWordReader.cs | 8 + TagCloud/WordReaders/WordReader.cs | 22 +++ di.sln | 20 +- 30 files changed, 978 insertions(+), 1 deletion(-) create mode 100644 TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs create mode 100644 TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs create mode 100644 TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs create mode 100644 TagCloud.Tests/Extensions/RectangleExtensions.cs create mode 100644 TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs create mode 100644 TagCloud.Tests/TagCloud.Tests.csproj create mode 100644 TagCloud.Tests/WordCountersTests/WordCounterTest.cs create mode 100644 TagCloud.Tests/WordReadersTests/WordReaderTest.cs create mode 100644 TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs create mode 100644 TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs create mode 100644 TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs create mode 100644 TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs create mode 100644 TagCloud/CloudLayouters/ICloudLayouter.cs create mode 100644 TagCloud/ImageSavers/IImageSaver.cs create mode 100644 TagCloud/ImageSavers/ImageSaver.cs create mode 100644 TagCloud/Normalizers/INormalizer.cs create mode 100644 TagCloud/Normalizers/Normalizer.cs create mode 100644 TagCloud/Program.cs create mode 100644 TagCloud/Tag.cs create mode 100644 TagCloud/TagCloud.csproj create mode 100644 TagCloud/WordCounters/IWordCounter.cs create mode 100644 TagCloud/WordCounters/WordCounter.cs create mode 100644 TagCloud/WordFilters/IWordFilter.cs create mode 100644 TagCloud/WordFilters/WordFilter.cs create mode 100644 TagCloud/WordReaders/IWordReader.cs create mode 100644 TagCloud/WordReaders/WordReader.cs diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs new file mode 100644 index 00000000..89695829 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -0,0 +1,177 @@ +using FluentAssertions; +using System.Drawing; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Tests.Extensions; + +namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests +{ + [TestFixture] + internal class CircularCloudLayouterMainRequirementsTest + { + private Point center; + private List rectangles; + private readonly string failedTestsDirectory = "FailedTest"; + + private readonly ImageSaver imageSaver = new ImageSaver(); + private readonly CloudLayouterPainter cloudLayouterPainter = new CloudLayouterPainter(); + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(failedTestsDirectory); + } + + [SetUp] + public void SetUp() + { + center = new Point(400, 400); + var minRectangleWidth = 30; + var maxRectangleWidth = 70; + var minRectangleHeight = 20; + var maxRectangleHeight = 50; + var rectanglesCount = 1000; + + rectangles = new List(); + var circularCloudLayouter = new CircularCloudLayouter(center); + + var randomWorker = new RandomCloudLayouterWorker( + minRectangleWidth, + maxRectangleWidth, + minRectangleHeight, + maxRectangleHeight); + foreach (var rectangleSize in randomWorker.GetNextRectangleSize(rectanglesCount)) + { + rectangles.Add(circularCloudLayouter.PutNextRectangle(rectangleSize)); + } + } + + [TestCase(0.7, 1000)] + [Repeat(10)] + public void ShouldPlaceRectanglesInCircle(double expectedCoverageRatio, int gridSize) + { + var maxRadius = rectangles.Max(r => r.GetMaxDistanceFromPointToRectangleAngles(center)); + var step = 2 * maxRadius / gridSize; + + var occupancyGrid = GetOccupancyGrid(gridSize, maxRadius, step); + + var actualCoverageRatio = GetOccupancyGridRatio(occupancyGrid, maxRadius, step); + actualCoverageRatio.Should().BeGreaterThanOrEqualTo(expectedCoverageRatio); + } + + [TestCase(15)] + [Repeat(10)] + public void ShouldPlaceCenterOfMassOfRectanglesNearCenter(int tolerance) + { + var centerX = rectangles.Average(r => r.Left + r.Width / 2.0); + var centerY = rectangles.Average(r => r.Top + r.Height / 2.0); + var actualCenter = new Point((int)centerX, (int)centerY); + + var distance = Math.Sqrt(Math.Pow(actualCenter.X - center.X, 2) + + Math.Pow(actualCenter.Y - center.Y, 2)); + + distance.Should().BeLessThanOrEqualTo(tolerance); + } + + [Test] + [Repeat(10)] + public void ShouldPlaceRectanglesWithoutOverlap() + { + for (var i = 0; i < rectangles.Count; i++) + { + for (var j = i + 1; j < rectangles.Count; j++) + { + Assert.That( + rectangles[i].IntersectsWith(rectangles[j]) == false, + $"Прямоугольники пересекаются:\n" + + $"{rectangles[i].ToString()}\n" + + $"{rectangles[j].ToString()}"); + } + } + } + + [TearDown] + public void Cleanup() + { + if (TestContext.CurrentContext.Result.FailCount == 0) + { + return; + } + + var name = $"{TestContext.CurrentContext.Test.Name}.png"; + var path = Path.Combine(failedTestsDirectory, name); + imageSaver.SaveFile(cloudLayouterPainter.Draw(rectangles), path); + Console.WriteLine($"Tag cloud visualization saved to file {path}"); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(failedTestsDirectory) + && Directory.GetFiles(failedTestsDirectory).Length == 0) + { + Directory.Delete(failedTestsDirectory); + } + } + + private (int start, int end) GetGridIndexesInterval( + int rectangleStartValue, + int rectangleCorrespondingSize, + double maxRadius, + double step) + { + var start = (int)((rectangleStartValue - center.X + maxRadius) / step); + var end = (int)((rectangleStartValue + rectangleCorrespondingSize - center.X + maxRadius) / step); + return (start, end); + } + + private bool[,] GetOccupancyGrid(int gridSize, double maxRadius, double step) + { + var result = new bool[gridSize, gridSize]; + foreach (var rect in rectangles) + { + var xInterval = GetGridIndexesInterval(rect.X, rect.Width, maxRadius, step); + var yInterval = GetGridIndexesInterval(rect.Y, rect.Height, maxRadius, step); + for (var x = xInterval.start; x <= xInterval.end; x++) + { + for (var y = yInterval.start; y <= yInterval.end; y++) + { + result[x, y] = true; + } + } + } + return result; + } + + private double GetOccupancyGridRatio(bool[,] occupancyGrid, double maxRadius, double step) + { + var totalCellsInsideCircle = 0; + var coveredCellsInsideCircle = 0; + for (var x = 0; x < occupancyGrid.GetLength(0); x++) + { + for (var y = 0; y < occupancyGrid.GetLength(0); y++) + { + var cellCenterX = x * step - maxRadius + center.X; + var cellCenterY = y * step - maxRadius + center.Y; + + var distance = Math.Sqrt( + Math.Pow(cellCenterX - center.X, 2) + Math.Pow(cellCenterY - center.Y, 2)); + + if (distance > maxRadius) + { + continue; + } + + totalCellsInsideCircle += 1; + if (occupancyGrid[x, y]) + { + coveredCellsInsideCircle += 1; + } + } + } + return (double)coveredCellsInsideCircle / totalCellsInsideCircle; + } + } +} diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs new file mode 100644 index 00000000..2ce625f2 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs @@ -0,0 +1,22 @@ +using System.Drawing; +using TagCloud.CloudLayouters.CircularCloudLayouter; + +namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests +{ + [TestFixture] + internal class CircularCloudLayouterTest + { + [TestCase(0, 100)] + [TestCase(-1, 100)] + [TestCase(100, 0)] + [TestCase(100, -1)] + public void PutNextRectangle_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + int width, + int height) + { + var size = new Size(width, height); + Assert.Throws( + () => new CircularCloudLayouter(new Point()).PutNextRectangle(size)); + } + } +} diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs new file mode 100644 index 00000000..f5ea045a --- /dev/null +++ b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs @@ -0,0 +1,30 @@ +using TagCloud.CloudLayouterWorkers; + +namespace TagCloud.Tests.CloudLayouterWorkersTests +{ + [TestFixture] + internal class CircularCloudLayouterWorkerTests + { + [TestCase(0, 100)] + [TestCase(-1, 100)] + [TestCase(100, 0)] + [TestCase(100, -1)] + public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize(int width, int height) + { + Assert.Throws( + () => new RandomCloudLayouterWorker(width, width, height, height)); + } + + [TestCase(50, 25, 25, 50)] + [TestCase(25, 50, 50, 25)] + public void GetNextRectangleSize_ThrowsArgumentException_OnNonConsecutiveSizeValues( + int minWidth, + int maxWidth, + int minHeight, + int maxHeight) + { + Assert.Throws( + () => new RandomCloudLayouterWorker(minWidth, maxWidth, minHeight, maxHeight)); + } + } +} diff --git a/TagCloud.Tests/Extensions/RectangleExtensions.cs b/TagCloud.Tests/Extensions/RectangleExtensions.cs new file mode 100644 index 00000000..1b567b7b --- /dev/null +++ b/TagCloud.Tests/Extensions/RectangleExtensions.cs @@ -0,0 +1,16 @@ +using System.Drawing; + +namespace TagCloud.Tests.Extensions +{ + internal static class RectangleExtensions + { + public static double GetMaxDistanceFromPointToRectangleAngles(this Rectangle rectangle, Point point) + { + var dx = Math.Max( + Math.Abs(rectangle.X - point.X), Math.Abs(rectangle.X + rectangle.Width - point.X)); + var dy = Math.Max( + Math.Abs(rectangle.Y - point.Y), Math.Abs(rectangle.Y + rectangle.Height - point.Y)); + return Math.Sqrt(dx * dx + dy * dy); + } + } +} diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs new file mode 100644 index 00000000..983146b0 --- /dev/null +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -0,0 +1,61 @@ +using System.Drawing; +using TagCloud.ImageSavers; + +namespace TagCloud.Tests.ImageSaversTests +{ + [TestFixture] + internal class ImageSaverTest + { + private string directoryPath = "TempFilesForImageSaverTests"; + private ImageSaver imageSaver; + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(directoryPath); + } + + [SetUp] + public void SetUp() + { + imageSaver = new ImageSaver(); + } + + [TestCase("Test.png")] + public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) + { + var path = Path.Combine(directoryPath, filename); + Assert.Throws(() => imageSaver.SaveFile(null, path)); + } + + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void SaveFile_ThrowsArgumentException_WithInvalidFilename(string? filename) + { + var dummyImage = new Bitmap(1, 1); + Assert.Throws(() => imageSaver.SaveFile(dummyImage, filename)); + } + + [TestCase("Test.png", ExpectedResult = true)] + public bool SaveFile_SavesFile(string filename) + { + var dummyImage = new Bitmap(1, 1); + var path = Path.Combine(directoryPath, filename); + + File.Delete(path); + imageSaver.SaveFile(dummyImage, path); + return File.Exists(path); + } + + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} diff --git a/TagCloud.Tests/TagCloud.Tests.csproj b/TagCloud.Tests/TagCloud.Tests.csproj new file mode 100644 index 00000000..391e86bc --- /dev/null +++ b/TagCloud.Tests/TagCloud.Tests.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs new file mode 100644 index 00000000..1e55aabf --- /dev/null +++ b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs @@ -0,0 +1,46 @@ +using FluentAssertions; +using TagCloud.WordCounters; + +namespace TagCloud.Tests.WordCountersTests +{ + [TestFixture] + internal class WordCounterTest + { + private WordCounter wordCounter; + + [SetUp] + public void SetUp() + { + wordCounter = new WordCounter(); + } + + [Test] + public void WordCounter_CountsCorrect() + { + var expected = new Dictionary() + { + { "One", 2 }, + { "Two", 1 }, + { "Three", 1 }, + { "Four", 4 }, + }; + var values = new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }; + + foreach (var value in values) + { + wordCounter.AddWord(value); + } + wordCounter.Counts.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs new file mode 100644 index 00000000..1fa3f690 --- /dev/null +++ b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs @@ -0,0 +1,73 @@ +using Microsoft.VisualBasic; +using TagCloud.WordReaders; + +namespace TagCloud.Tests.WordReadersTests +{ + [TestFixture] + internal class WordReaderTest + { + private readonly string directoryPath = "TempFilesForWordReaderTests"; + + private readonly string fileWithCorrectValuesPath = "correctFile.txt"; + private readonly string[] correctValues = new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }; + + private readonly string fileWithIncorrectValuesPath = "incorrectFile.txt"; + private readonly string[] incorrectValues = new string[] + { + "One", + "Two", + "Three Three", + "Four" + }; + + private WordReader wordReader; + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(directoryPath); + File.WriteAllLines(Path.Combine(directoryPath, fileWithCorrectValuesPath), correctValues); + File.WriteAllLines(Path.Combine(directoryPath, fileWithIncorrectValuesPath), incorrectValues); + } + + [SetUp] + public void SetUp() + { + wordReader = new WordReader(); + } + + [TestCase(" ")] + [TestCase("ThisFileDoesNotExist.txt")] + public void WordReader_ThrowsFileNotFoundException_WithInvalidFilename(string filename) + { + var path = Path.Combine(directoryPath, filename); + Assert.Throws(() => wordReader.ReadByLines(path).ToArray()); + } + + [Test] + public void WordReader_ThrowsException_WithTwoWordsInOneLine() + { + var path = Path.Combine(directoryPath, fileWithIncorrectValuesPath); + Assert.Throws(() => wordReader.ReadByLines(path).ToArray()); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs new file mode 100644 index 00000000..67ab946b --- /dev/null +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -0,0 +1,57 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterPainters +{ + // Класс, со старого задания TagCloud, + // Который отрисовывает прямоугольники. + // В текущей реализации размер изображения определяется автоматически, + // А первый поставленный прямоугольник всегда находится в центре изображения + internal class CloudLayouterPainter( + Color? backgroundColor = null, + Color? rectangleBorderColor = null, + int? paddingPerSide = null) : ICloudLayouterPainter + { + private readonly int paddingPerSide = paddingPerSide ?? 10; + private readonly Color backgroundColor = backgroundColor ?? Color.White; + private readonly Color rectangleBorderColor = rectangleBorderColor ?? Color.Black; + + public Bitmap Draw(IList rectangles) + { + if (rectangles.Count == 0) + { + throw new ArgumentException("Список прямоугольников пуст."); + } + + var minimums = new Point(rectangles.Min(r => r.Left), rectangles.Min(r => r.Top)); + var maximums = new Point(rectangles.Max(r => r.Right), rectangles.Max(r => r.Bottom)); + + var imageSize = GetImageSize(minimums, maximums, paddingPerSide); + var result = new Bitmap(imageSize.Width, imageSize.Height); + + using var graphics = Graphics.FromImage(result); + graphics.Clear(backgroundColor); + using var pen = new Pen(rectangleBorderColor, 1); + for (var i = 0; i < rectangles.Count; i++) + { + var positionOnCanvas = GetPositionOnCanvas( + rectangles[i], + minimums, + paddingPerSide); + graphics.DrawRectangle( + pen, + positionOnCanvas.X, + positionOnCanvas.Y, + rectangles[i].Width, + rectangles[i].Height); + } + + return result; + } + + private Point GetPositionOnCanvas(Rectangle rectangle, Point minimums, int padding) + => new Point(rectangle.X - minimums.X + padding, rectangle.Y - minimums.Y + padding); + + private Size GetImageSize(Point minimums, Point maximums, int paddingPerSide) + => new Size(maximums.X - minimums.X + 2 * paddingPerSide, maximums.Y - minimums.Y + 2 * paddingPerSide); + } +} diff --git a/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs new file mode 100644 index 00000000..3f4a5a16 --- /dev/null +++ b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterPainters +{ + // Интерфейс отрисовки прямоугольников + internal interface ICloudLayouterPainter + { + public Bitmap Draw(IList rectangles); + } +} diff --git a/TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs new file mode 100644 index 00000000..1c52526c --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs @@ -0,0 +1,17 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + internal class FrequencyBasedCloudLayouterWorker( + int minRectangleWidth, + int maxRectangleWidth, + int minRectangleHeight, + int maxRectangleHeight, + Dictionary normalizedValues) : ICloudLayouterWorker + { + public IEnumerable GetNextRectangleSize(int rectanglesCount) + { + throw new NotImplementedException(); + } + } +} diff --git a/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs new file mode 100644 index 00000000..ecca5dc6 --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + // Интерфейс получения размера следующего прямоугольника + internal interface ICloudLayouterWorker + { + public IEnumerable GetNextRectangleSize(int rectanglesCount); + } +} diff --git a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs new file mode 100644 index 00000000..2e5c80d8 --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs @@ -0,0 +1,51 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + // Класс, со старого задания TagCloud, + // выдающий случайный размер прямоугольника + internal class RandomCloudLayouterWorker : ICloudLayouterWorker + { + private Random random = new Random(); + public readonly int MinRectangleWidth; + public readonly int MaxRectangleWidth; + public readonly int MinRectangleHeight; + public readonly int MaxRectangleHeight; + + public RandomCloudLayouterWorker( + int minRectangleWidth, + int maxRectangleWidth, + int minRectangleHeight, + int maxRectangleHeight) + { + if (minRectangleWidth <= 0 || maxRectangleWidth <= 0 + || minRectangleHeight <= 0 || maxRectangleHeight <= 0) + { + throw new ArgumentException( + "Ширина или высота прямоугольника должна быть положительной"); + } + + if (minRectangleWidth > maxRectangleWidth + || minRectangleHeight > maxRectangleHeight) + { + throw new ArgumentException( + "Минимальное значение ширины или высоты не может быть больше максимального"); + } + + MinRectangleWidth = minRectangleWidth; + MaxRectangleWidth = maxRectangleWidth; + MinRectangleHeight = minRectangleHeight; + MaxRectangleHeight = maxRectangleHeight; + } + + public IEnumerable GetNextRectangleSize(int rectanglesCount) + { + for (var i = 0; i < rectanglesCount; i++) + { + var width = random.Next(MinRectangleWidth, MaxRectangleWidth); + var height = random.Next(MinRectangleHeight, MaxRectangleHeight); + yield return new Size(width, height); + } + } + } +} diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs new file mode 100644 index 00000000..07d1025c --- /dev/null +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs @@ -0,0 +1,24 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouters.CircularCloudLayouter +{ + internal class Circle(Point center, float startRadius = 2.0f) + { + public float Radius { get; set; } = startRadius; + + public IEnumerable GetCoordinatesOnCircle( + int startAngle, + int step = 1) + { + for (var dAngle = 0; dAngle < 360; dAngle += step) + { + var angle = (startAngle + dAngle) % 360; + + double angleInRadians = angle * Math.PI / 180; + var x = (int)(center.X + Radius * Math.Cos(angleInRadians)); + var y = (int)(center.Y + Radius * Math.Sin(angleInRadians)); + yield return new Point(x, y); + } + } + } +} diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs new file mode 100644 index 00000000..ec9c1ea6 --- /dev/null +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs @@ -0,0 +1,68 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouters.CircularCloudLayouter +{ + // Класс, со старого задания TagCloud, + // который расставляет прямоугольники по окружности + // с постепенно увеличивающимся радиусом + internal class CircularCloudLayouter(Point center) : ICloudLayouter + { + private readonly Circle arrangementСircle = new Circle(center); + private readonly Random random = new Random(); + private readonly List rectangles = new List(); + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) + { + throw new ArgumentException( + "Размеры прямоугольника не могут быть меньше либо равны нуля."); + } + + var result = new Rectangle(); + arrangementСircle.Radius -= 1.0f; + + var isPlaced = false; + while (!isPlaced) + { + var startAngle = random.Next(360); + foreach (var coordinate in arrangementСircle.GetCoordinatesOnCircle(startAngle)) + { + var location = GetRectangleLocation(coordinate, rectangleSize); + var nextRectangle = new Rectangle(location, rectangleSize); + if (!IsIntersectionWithAlreadyPlaced(nextRectangle)) + { + rectangles.Add(nextRectangle); + isPlaced = true; + result = nextRectangle; + break; + } + } + + arrangementСircle.Radius += 1.0f; + } + + return result; + } + + private bool IsIntersectionWithAlreadyPlaced(Rectangle rectangle) + { + foreach (var rect in rectangles) + { + if (rect.IntersectsWith(rectangle)) + { + return true; + } + } + + return false; + } + + private Point GetRectangleLocation(Point pointOnCircle, Size rectangleSize) + { + var x = pointOnCircle.X - rectangleSize.Width / 2; + var y = pointOnCircle.Y - rectangleSize.Height / 2; + return new Point(x, y); + } + } +} diff --git a/TagCloud/CloudLayouters/ICloudLayouter.cs b/TagCloud/CloudLayouters/ICloudLayouter.cs new file mode 100644 index 00000000..49abc795 --- /dev/null +++ b/TagCloud/CloudLayouters/ICloudLayouter.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouters +{ + // Интерфейс расстановки прямоугольников + internal interface ICloudLayouter + { + public Rectangle PutNextRectangle(Size rectangleSize); + } +} diff --git a/TagCloud/ImageSavers/IImageSaver.cs b/TagCloud/ImageSavers/IImageSaver.cs new file mode 100644 index 00000000..75ed6117 --- /dev/null +++ b/TagCloud/ImageSavers/IImageSaver.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.ImageSavers +{ + // Интерфейс сохранения изображения в файл + internal interface IImageSaver + { + public void SaveFile(Bitmap image, string fileName); + } +} diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs new file mode 100644 index 00000000..1194f69e --- /dev/null +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -0,0 +1,22 @@ +using System.Drawing; + +namespace TagCloud.ImageSavers +{ + internal class ImageSaver : IImageSaver + { + public void SaveFile(Bitmap image, string fileName) + { + if (image is null) + { + throw new ArgumentNullException("Передаваемое изображение не должно быть null"); + } + + if (string.IsNullOrWhiteSpace(fileName)) + { + throw new ArgumentException("Некорректное имя файла для создания"); + } + + image.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); + } + } +} diff --git a/TagCloud/Normalizers/INormalizer.cs b/TagCloud/Normalizers/INormalizer.cs new file mode 100644 index 00000000..32abc7f2 --- /dev/null +++ b/TagCloud/Normalizers/INormalizer.cs @@ -0,0 +1,8 @@ +namespace TagCloud.Normalizers +{ + // Интерфейс нормализации количества каждого слова + internal interface INormalizer + { + public Dictionary Normalize(Dictionary values); + } +} diff --git a/TagCloud/Normalizers/Normalizer.cs b/TagCloud/Normalizers/Normalizer.cs new file mode 100644 index 00000000..5517929f --- /dev/null +++ b/TagCloud/Normalizers/Normalizer.cs @@ -0,0 +1,10 @@ +namespace TagCloud.Normalizers +{ + internal class Normalizer : INormalizer + { + public Dictionary Normalize(Dictionary values) + { + throw new NotImplementedException(); + } + } +} diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs new file mode 100644 index 00000000..61b80b47 --- /dev/null +++ b/TagCloud/Program.cs @@ -0,0 +1,106 @@ +using System.Drawing; +using System.Runtime.CompilerServices; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +[assembly: InternalsVisibleTo("TagCloud.Tests")] +namespace TagCloud +{ + internal class Program + { + static void Main(string[] args) + { + StartOldVariant(); + } + + // Пример работы со старого задания TagCloud + private static void StartOldVariant() + { + var center = new Point(400, 400); + var minRectangleWidth = 30; + var maxRectangleWidth = 70; + var minRectangleHeight = 20; + var maxRectangleHeight = 50; + var rectanglesCount = 1000; + var imageFileName = "Result.png"; + + var rectangles = new List(); + var circularCloudLayouter = new CircularCloudLayouter(center); + + var randomWorker = new RandomCloudLayouterWorker( + minRectangleWidth, + maxRectangleWidth, + minRectangleHeight, + maxRectangleHeight); + + foreach (var rectangleSize in randomWorker.GetNextRectangleSize(rectanglesCount)) + { + rectangles.Add(circularCloudLayouter.PutNextRectangle(rectangleSize)); + } + + var painter = new CloudLayouterPainter(); + var imageSaver = new ImageSaver(); + imageSaver.SaveFile(painter.Draw(rectangles), imageFileName); + } + + // Пример работы с новым вариантом + private static void StartNewVariant() + { + var center = new Point(400, 400); + var minRectangleWidth = 30; + var maxRectangleWidth = 70; + var minRectangleHeight = 20; + var maxRectangleHeight = 50; + + var imageFileName = "Result.png"; + var dataFileName = "Values.txt"; + + var wordReader = new WordReader(); + var wordFilter = new WordFilter(); + var wordCounter = new WordCounter(); + + foreach (var word in wordReader.ReadByLines(dataFileName)) + { + if (!wordFilter.IsCorrectWord(word)) + { + continue; + } + wordCounter.AddWord(word); + } + + var normalizer = new Normalizer(); + var normalizedWordCounts = normalizer.Normalize(wordCounter.Counts); + + var circularCloudLayouter = new CircularCloudLayouter(center); + var frequencyBasedCloudLayouterWorker = + new FrequencyBasedCloudLayouterWorker( + minRectangleWidth, + maxRectangleWidth, + minRectangleHeight, + maxRectangleHeight, + normalizedWordCounts); + + // Начиная с этого места, + // в дальнейшем нужно будет использовать + // Tag вместо Rectangle. + // В черновом варианте не переделывал, + // что бы не ломать старые написанные тесты. + var rectangles = new List(); + foreach (var rectangleSize in frequencyBasedCloudLayouterWorker + .GetNextRectangleSize(wordCounter.Counts.Count)) + { + rectangles.Add(circularCloudLayouter.PutNextRectangle(rectangleSize)); + } + + var painter = new CloudLayouterPainter(); + var imageSaver = new ImageSaver(); + imageSaver.SaveFile(painter.Draw(rectangles), imageFileName); + } + } +} diff --git a/TagCloud/Tag.cs b/TagCloud/Tag.cs new file mode 100644 index 00000000..5a0d4403 --- /dev/null +++ b/TagCloud/Tag.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagCloud +{ + // В дальнейшем буду работать с сущностью Tag, + // имеющей похожую структуру + internal record Tag(string Text, Rectangle Rectangle); +} diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj new file mode 100644 index 00000000..71159cfa --- /dev/null +++ b/TagCloud/TagCloud.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/TagCloud/WordCounters/IWordCounter.cs b/TagCloud/WordCounters/IWordCounter.cs new file mode 100644 index 00000000..70523e3c --- /dev/null +++ b/TagCloud/WordCounters/IWordCounter.cs @@ -0,0 +1,9 @@ +namespace TagCloud.WordCounters +{ + // Интерфейс подсчёта количества каждого уникального слова + internal interface IWordCounter + { + public void AddWord(string word); + public Dictionary Counts { get; } + } +} diff --git a/TagCloud/WordCounters/WordCounter.cs b/TagCloud/WordCounters/WordCounter.cs new file mode 100644 index 00000000..ea4350be --- /dev/null +++ b/TagCloud/WordCounters/WordCounter.cs @@ -0,0 +1,18 @@ +namespace TagCloud.WordCounters +{ + internal class WordCounter : IWordCounter + { + private readonly Dictionary counts = new Dictionary(); + public Dictionary Counts => counts; + + public void AddWord(string word) + { + if (!counts.ContainsKey(word)) + { + counts[word] = 1; + return; + } + counts[word] += 1; + } + } +} diff --git a/TagCloud/WordFilters/IWordFilter.cs b/TagCloud/WordFilters/IWordFilter.cs new file mode 100644 index 00000000..4e175d56 --- /dev/null +++ b/TagCloud/WordFilters/IWordFilter.cs @@ -0,0 +1,8 @@ +namespace TagCloud.WordFilters +{ + // Интерфейс фильтрации "скучных" слов + internal interface IWordFilter + { + public bool IsCorrectWord(string word); + } +} diff --git a/TagCloud/WordFilters/WordFilter.cs b/TagCloud/WordFilters/WordFilter.cs new file mode 100644 index 00000000..6ce55e2d --- /dev/null +++ b/TagCloud/WordFilters/WordFilter.cs @@ -0,0 +1,10 @@ +namespace TagCloud.WordFilters +{ + internal class WordFilter : IWordFilter + { + public bool IsCorrectWord(string word) + { + throw new NotImplementedException(); + } + } +} diff --git a/TagCloud/WordReaders/IWordReader.cs b/TagCloud/WordReaders/IWordReader.cs new file mode 100644 index 00000000..5384df2a --- /dev/null +++ b/TagCloud/WordReaders/IWordReader.cs @@ -0,0 +1,8 @@ +namespace TagCloud.WordReaders +{ + // Интерфейс для построчного чтения содержимого файла + internal interface IWordReader + { + public IEnumerable ReadByLines(string path); + } +} diff --git a/TagCloud/WordReaders/WordReader.cs b/TagCloud/WordReaders/WordReader.cs new file mode 100644 index 00000000..4443209a --- /dev/null +++ b/TagCloud/WordReaders/WordReader.cs @@ -0,0 +1,22 @@ +namespace TagCloud.WordReaders +{ + internal class WordReader : IWordReader + { + public IEnumerable ReadByLines(string path) + { + if (!File.Exists(path)) + { + throw new FileNotFoundException($"Файл {path} не существует"); + } + + foreach (var line in File.ReadAllLines(path)) + { + if (line.Contains(' ')) + { + throw new Exception($"Файл {path} содержит строку с двумя и более словами"); + } + yield return line; + } + } + } +} diff --git a/di.sln b/di.sln index a50991da..70f2aa48 100644 --- a/di.sln +++ b/di.sln @@ -1,6 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "di", "FractalPainter/di.csproj", "{4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}" +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "di", "FractalPainter\di.csproj", "{4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloud", "TagCloud\TagCloud.csproj", "{DBAE2C15-6BFD-46B8-955E-C169F7BA6FB7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloud.Tests", "TagCloud.Tests\TagCloud.Tests.csproj", "{99BFD9ED-99EC-41F6-97AA-CCF638BB9ED6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -12,5 +19,16 @@ Global {4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU {4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU {4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}.Release|Any CPU.Build.0 = Release|Any CPU + {DBAE2C15-6BFD-46B8-955E-C169F7BA6FB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBAE2C15-6BFD-46B8-955E-C169F7BA6FB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBAE2C15-6BFD-46B8-955E-C169F7BA6FB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBAE2C15-6BFD-46B8-955E-C169F7BA6FB7}.Release|Any CPU.Build.0 = Release|Any CPU + {99BFD9ED-99EC-41F6-97AA-CCF638BB9ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99BFD9ED-99EC-41F6-97AA-CCF638BB9ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99BFD9ED-99EC-41F6-97AA-CCF638BB9ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99BFD9ED-99EC-41F6-97AA-CCF638BB9ED6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection EndGlobal From 8b6b473cd748ba2979b146f221e62d38c43e42e8 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:31:26 +0500 Subject: [PATCH 02/17] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB,=20=D0=BE=D1=82=D0=B2=D0=B5=D1=87?= =?UTF-8?q?=D0=B0=D1=8E=D1=89=D0=B8=D0=B9=20=D0=B7=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D1=81=D0=BE=D0=B2=D0=BA=D1=83=20=D0=BF=D1=80=D1=8F?= =?UTF-8?q?=D0=BC=D0=BE=D1=83=D0=B3=D0=BE=D0=BB=D1=8C=D0=BD=D0=B8=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainter.cs | 88 +++++++++++++------ .../ICloudLayouterPainter.cs | 2 +- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index 67ab946b..6c46b381 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -2,56 +2,86 @@ namespace TagCloud.CloudLayouterPainters { - // Класс, со старого задания TagCloud, - // Который отрисовывает прямоугольники. - // В текущей реализации размер изображения определяется автоматически, - // А первый поставленный прямоугольник всегда находится в центре изображения internal class CloudLayouterPainter( + Size imageSize, Color? backgroundColor = null, - Color? rectangleBorderColor = null, - int? paddingPerSide = null) : ICloudLayouterPainter + Color? textColor = null, + string? fontName = null) : ICloudLayouterPainter { - private readonly int paddingPerSide = paddingPerSide ?? 10; private readonly Color backgroundColor = backgroundColor ?? Color.White; - private readonly Color rectangleBorderColor = rectangleBorderColor ?? Color.Black; + private readonly Color textColor = textColor ?? Color.Black; + private readonly string fontName = fontName ?? "Arial"; - public Bitmap Draw(IList rectangles) + public Bitmap Draw(IList tags) { - if (rectangles.Count == 0) + ArgumentNullException.ThrowIfNull(tags); + + if (tags.Count == 0) { - throw new ArgumentException("Список прямоугольников пуст."); + throw new ArgumentException("Список тегов пуст"); } - var minimums = new Point(rectangles.Min(r => r.Left), rectangles.Min(r => r.Top)); - var maximums = new Point(rectangles.Max(r => r.Right), rectangles.Max(r => r.Bottom)); - - var imageSize = GetImageSize(minimums, maximums, paddingPerSide); var result = new Bitmap(imageSize.Width, imageSize.Height); using var graphics = Graphics.FromImage(result); graphics.Clear(backgroundColor); - using var pen = new Pen(rectangleBorderColor, 1); - for (var i = 0; i < rectangles.Count; i++) + + foreach (var tag in tags) { - var positionOnCanvas = GetPositionOnCanvas( - rectangles[i], - minimums, - paddingPerSide); - graphics.DrawRectangle( - pen, + var positionOnCanvas = GetPositionOnCanvas(tag.Rectangle); + var rectOnCanvas = new Rectangle( positionOnCanvas.X, positionOnCanvas.Y, - rectangles[i].Width, - rectangles[i].Height); + tag.Rectangle.Width, + tag.Rectangle.Height); + DrawText(graphics, rectOnCanvas, tag.Text); } return result; } - private Point GetPositionOnCanvas(Rectangle rectangle, Point minimums, int padding) - => new Point(rectangle.X - minimums.X + padding, rectangle.Y - minimums.Y + padding); + private Point GetPositionOnCanvas(Rectangle rectangle) + => new Point(rectangle.X + imageSize.Width / 2, rectangle.Y + imageSize.Height / 2); + + private void DrawText(Graphics graphics, Rectangle rectangle, string text) + { + var fontSize = FindFittingFontSize(graphics, text, rectangle); + var fittingFont = new Font(fontName, fontSize, FontStyle.Regular, GraphicsUnit.Pixel); + + using var stringFormat = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + + using var brush = new SolidBrush(textColor); + graphics.DrawString(text, fittingFont, brush, rectangle, stringFormat); + } - private Size GetImageSize(Point minimums, Point maximums, int paddingPerSide) - => new Size(maximums.X - minimums.X + 2 * paddingPerSide, maximums.Y - minimums.Y + 2 * paddingPerSide); + private int FindFittingFontSize(Graphics graphics, string text, Rectangle rectangle) + { + var minSize = 1; + var maxSize = Math.Min(rectangle.Width, rectangle.Height); + var result = minSize; + + while (minSize <= maxSize) + { + var midSize = (minSize + maxSize) / 2; + using var font = new Font(fontName, midSize, FontStyle.Regular, GraphicsUnit.Pixel); + + var textSize = graphics.MeasureString(text, font); + if (textSize.Width <= rectangle.Width && textSize.Height <= rectangle.Height) + { + result = midSize; + minSize = midSize + 1; + } + else + { + maxSize = midSize - 1; + } + } + + return result; + } } } diff --git a/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs index 3f4a5a16..38fe25ab 100644 --- a/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs @@ -5,6 +5,6 @@ namespace TagCloud.CloudLayouterPainters // Интерфейс отрисовки прямоугольников internal interface ICloudLayouterPainter { - public Bitmap Draw(IList rectangles); + public Bitmap Draw(IList tags); } } From dd659598a9c053f691192a08c81e628f4dbb61b0 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:32:14 +0500 Subject: [PATCH 03/17] =?UTF-8?q?=D0=A0=D0=B5=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=20=D1=80=D0=B0=D1=81=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BF=D1=80=D1=8F=D0=BC=D0=BE=D1=83?= =?UTF-8?q?=D0=B3=D0=BE=D0=BB=D1=8C=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=84=D0=BE=D1=80=D0=BC=D0=B5=20=D0=BE=D0=BA=D1=80?= =?UTF-8?q?=D1=83=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouters/CircularCloudLayouter/Circle.cs | 3 ++- .../CircularCloudLayouter/CircularCloudLayouter.cs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs index 07d1025c..55198b98 100644 --- a/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs @@ -2,8 +2,9 @@ namespace TagCloud.CloudLayouters.CircularCloudLayouter { - internal class Circle(Point center, float startRadius = 2.0f) + internal class Circle(float startRadius = 2.0f) { + private readonly Point center = new Point(0, 0); public float Radius { get; set; } = startRadius; public IEnumerable GetCoordinatesOnCircle( diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs index ec9c1ea6..7809e045 100644 --- a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs @@ -4,10 +4,17 @@ namespace TagCloud.CloudLayouters.CircularCloudLayouter { // Класс, со старого задания TagCloud, // который расставляет прямоугольники по окружности - // с постепенно увеличивающимся радиусом - internal class CircularCloudLayouter(Point center) : ICloudLayouter + // с постепенно увеличивающимся радиусом. + // Прямоугольники расставляются вокруг точки с координатой (0, 0), + // Затем, в CloudLayouterPainter координат пересыитываются таким образом, + // что бы расположить первый прямоугольник в центре холста. + // Можно создать интерфейс IShape, который через GetCoordinates + // будет возвращать координаты линии формы. + // Тогда Circle можно заменить на IShape и ввести новые формы расстановки. + + internal class CircularCloudLayouter : ICloudLayouter { - private readonly Circle arrangementСircle = new Circle(center); + private readonly Circle arrangementСircle = new Circle(); private readonly Random random = new Random(); private readonly List rectangles = new List(); From 8a2b6fb8a4cb4087b17b94051aa59f002b425cb9 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:33:03 +0500 Subject: [PATCH 04/17] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=81=D0=B2=D0=BE=D0=B9=D1=81=D1=82=D0=B2?= =?UTF-8?q?=20=D1=81=D0=BB=D0=B5=D0=B4=D1=83=D1=8E=D1=89=D0=B5=D0=B3=D0=BE?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B0=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B5=20=D0=BF=D1=80=D1=8F=D0=BC=D0=BE=D1=83=D0=B3?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=BD=D0=B8=D0=BA=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FrequencyBasedCloudLayouterWorker.cs | 17 ------- .../ICloudLayouterWorker.cs | 6 ++- ...alizedFrequencyBasedCloudLayouterWorker.cs | 49 +++++++++++++++++++ .../RandomCloudLayouterWorker.cs | 7 +-- 4 files changed, 57 insertions(+), 22 deletions(-) delete mode 100644 TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs diff --git a/TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs deleted file mode 100644 index 1c52526c..00000000 --- a/TagCloud/CloudLayouterWorkers/FrequencyBasedCloudLayouterWorker.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Drawing; - -namespace TagCloud.CloudLayouterWorkers -{ - internal class FrequencyBasedCloudLayouterWorker( - int minRectangleWidth, - int maxRectangleWidth, - int minRectangleHeight, - int maxRectangleHeight, - Dictionary normalizedValues) : ICloudLayouterWorker - { - public IEnumerable GetNextRectangleSize(int rectanglesCount) - { - throw new NotImplementedException(); - } - } -} diff --git a/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs index ecca5dc6..befcb3a4 100644 --- a/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs @@ -2,9 +2,11 @@ namespace TagCloud.CloudLayouterWorkers { - // Интерфейс получения размера следующего прямоугольника + // Интерфейс получения свойств следующего прямоугольника + // По хорошему, нужно возвращать IEnumerable, + // для повышения возможности переиспользования internal interface ICloudLayouterWorker { - public IEnumerable GetNextRectangleSize(int rectanglesCount); + public IEnumerable<(string word, Size size)> GetNextRectangleProperties(); } } diff --git a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs new file mode 100644 index 00000000..8bda8ce4 --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs @@ -0,0 +1,49 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + internal class NormalizedFrequencyBasedCloudLayouterWorker : ICloudLayouterWorker + { + public readonly int MaxRectangleWidth; + public readonly int MaxRectangleHeight; + private readonly Dictionary values; + private readonly string[] keysOrder; + public string[] KeysOrder => keysOrder; + + public NormalizedFrequencyBasedCloudLayouterWorker( + int maxRectangleWidth, + int maxRectangleHeight, + Dictionary normalizedValues, + bool isSorted = true) + { + if (maxRectangleWidth <= 0 || maxRectangleHeight <= 0) + { + throw new ArgumentException( + "Ширина или высота прямоугольника должна быть положительной"); + } + + MaxRectangleWidth = maxRectangleWidth; + MaxRectangleHeight = maxRectangleHeight; + values = normalizedValues; + if (isSorted) + { + keysOrder = values.OrderByDescending(x => x.Value).Select(x => x.Key).ToArray(); + } + else + { + keysOrder = values.Keys.ToArray(); + } + } + + public IEnumerable<(string word, Size size)> GetNextRectangleProperties() + { + foreach (var key in keysOrder) + { + var value = values[key]; + var width = (int)(MaxRectangleWidth * value); + var height = (int)(MaxRectangleHeight * value); + yield return (key, new Size(width, height)); + } + } + } +} diff --git a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs index 2e5c80d8..b05b6a47 100644 --- a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs @@ -4,6 +4,7 @@ namespace TagCloud.CloudLayouterWorkers { // Класс, со старого задания TagCloud, // выдающий случайный размер прямоугольника + // Оставил его для пары тестов. internal class RandomCloudLayouterWorker : ICloudLayouterWorker { private Random random = new Random(); @@ -38,13 +39,13 @@ public RandomCloudLayouterWorker( MaxRectangleHeight = maxRectangleHeight; } - public IEnumerable GetNextRectangleSize(int rectanglesCount) + public IEnumerable<(string word, Size size)> GetNextRectangleProperties() { - for (var i = 0; i < rectanglesCount; i++) + while (true) { var width = random.Next(MinRectangleWidth, MaxRectangleWidth); var height = random.Next(MinRectangleHeight, MaxRectangleHeight); - yield return new Size(width, height); + yield return (string.Empty, new Size(width, height)); } } } From 2ea4783ba5b08a834d874f409f7a2d9f60b64ec5 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:33:26 +0500 Subject: [PATCH 05/17] =?UTF-8?q?=D0=A0=D0=B5=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B8=D1=82=D0=BE=D0=B3=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=B8=D0=B7=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud/ImageSavers/IImageSaver.cs | 2 +- TagCloud/ImageSavers/ImageSaver.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/TagCloud/ImageSavers/IImageSaver.cs b/TagCloud/ImageSavers/IImageSaver.cs index 75ed6117..794e0bc0 100644 --- a/TagCloud/ImageSavers/IImageSaver.cs +++ b/TagCloud/ImageSavers/IImageSaver.cs @@ -5,6 +5,6 @@ namespace TagCloud.ImageSavers // Интерфейс сохранения изображения в файл internal interface IImageSaver { - public void SaveFile(Bitmap image, string fileName); + public void SaveFile(Bitmap image, string fileName, string format = "png"); } } diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs index 1194f69e..f5d2a082 100644 --- a/TagCloud/ImageSavers/ImageSaver.cs +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -2,9 +2,12 @@ namespace TagCloud.ImageSavers { + // Реализован пункт на перспективу: + // Формат результата. + // Поддерживать разные форматы изображений. internal class ImageSaver : IImageSaver { - public void SaveFile(Bitmap image, string fileName) + public void SaveFile(Bitmap image, string fileName, string format = "png") { if (image is null) { @@ -16,7 +19,7 @@ public void SaveFile(Bitmap image, string fileName) throw new ArgumentException("Некорректное имя файла для создания"); } - image.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); + image.Save($"{fileName}.{format}"); } } } From ea9307c3609291e70bf399aa7c6b8d9f68232029 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:33:47 +0500 Subject: [PATCH 06/17] =?UTF-8?q?=D0=A0=D0=B5=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=20=D0=BD=D0=BE=D1=80=D0=BC=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=87=D0=B0=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=82=D1=8B=20=D1=83=D0=BD=D0=B8=D0=BA=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D1=81=D0=BB=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud/Normalizers/INormalizer.cs | 5 ++- TagCloud/Normalizers/Normalizer.cs | 52 +++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/TagCloud/Normalizers/INormalizer.cs b/TagCloud/Normalizers/INormalizer.cs index 32abc7f2..050a7450 100644 --- a/TagCloud/Normalizers/INormalizer.cs +++ b/TagCloud/Normalizers/INormalizer.cs @@ -3,6 +3,9 @@ // Интерфейс нормализации количества каждого слова internal interface INormalizer { - public Dictionary Normalize(Dictionary values); + public Dictionary Normalize( + Dictionary values, + double minCoefficient = 0.25, + int decimalPlaces = 4); } } diff --git a/TagCloud/Normalizers/Normalizer.cs b/TagCloud/Normalizers/Normalizer.cs index 5517929f..6f7cd329 100644 --- a/TagCloud/Normalizers/Normalizer.cs +++ b/TagCloud/Normalizers/Normalizer.cs @@ -1,10 +1,58 @@ namespace TagCloud.Normalizers { + // Слово, которое встречается чаще всего, будет иметь вес 1.0. + // Это означает, что оно в дальнейшем будет иметь прямоугольник + // с максимальным размером. + // Слово с минимальной частотой будет иметь + // minCoefficient * максимальный размеро прямоугольника. internal class Normalizer : INormalizer { - public Dictionary Normalize(Dictionary values) + public Dictionary Normalize( + Dictionary values, + double minCoefficient = 0.25, + int decimalPlaces = 4) { - throw new NotImplementedException(); + if (minCoefficient < 0) + { + throw new ArgumentException("Минимальный коэффициент не может быть меньше 0"); + } + + var result = new Dictionary(); + + var maxValue = values.Values.Max(); + var minValue = values.Values.Min(); + + var scale = 1.0 - minCoefficient; + + foreach (var pair in values) + { + result[pair.Key] = CalculateNormalizedValue( + minCoefficient, + scale, + pair.Value, + minValue, + maxValue, + decimalPlaces); + } + + return result; + } + + private double CalculateNormalizedValue( + double minCoefficient, + double scale, + double value, + uint minValue, + uint maxValue, + int decimalPlaces) + { + if (minValue == maxValue) + { + return 1.0; + } + return Math.Round( + minCoefficient + scale * ((double)(value - minValue) / (maxValue - minValue)), + decimalPlaces); } } } From 39ce03faad11c0828f7de38db8c0c28c9d818ba4 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:34:59 +0500 Subject: [PATCH 07/17] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB=20=D0=BF=D0=BE=D0=B4=D1=81=D1=87=D1=91?= =?UTF-8?q?=D1=82=D0=B0=20=D1=83=D0=BD=D0=B8=D0=BA=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D1=81=D0=BB=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud/WordCounters/IWordCounter.cs | 2 +- TagCloud/WordCounters/WordCounter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TagCloud/WordCounters/IWordCounter.cs b/TagCloud/WordCounters/IWordCounter.cs index 70523e3c..f41d1763 100644 --- a/TagCloud/WordCounters/IWordCounter.cs +++ b/TagCloud/WordCounters/IWordCounter.cs @@ -4,6 +4,6 @@ internal interface IWordCounter { public void AddWord(string word); - public Dictionary Counts { get; } + public Dictionary Values { get; } } } diff --git a/TagCloud/WordCounters/WordCounter.cs b/TagCloud/WordCounters/WordCounter.cs index ea4350be..c5953a1f 100644 --- a/TagCloud/WordCounters/WordCounter.cs +++ b/TagCloud/WordCounters/WordCounter.cs @@ -3,7 +3,7 @@ internal class WordCounter : IWordCounter { private readonly Dictionary counts = new Dictionary(); - public Dictionary Counts => counts; + public Dictionary Values => counts; public void AddWord(string word) { From 416e7b9542600f27c7a395a27c1aa3691ed926ca Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:35:55 +0500 Subject: [PATCH 08/17] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BB=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud/WordFilters/WordFilter.cs | 67 +++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/TagCloud/WordFilters/WordFilter.cs b/TagCloud/WordFilters/WordFilter.cs index 6ce55e2d..add9c3eb 100644 --- a/TagCloud/WordFilters/WordFilter.cs +++ b/TagCloud/WordFilters/WordFilter.cs @@ -1,10 +1,75 @@ namespace TagCloud.WordFilters { + // Реализован пункт на перспективу: + // Предобработка слов. + // Дать возможность влиять на список скучных слов, которые не попадут в облако. internal class WordFilter : IWordFilter { + private readonly HashSet bannedWords = new HashSet() + { + // Просто скучные по моему мнению + "not", "also", "how", "let", + // To have + "have", "has", "had", "having", + // To be + "am", "is", "are", "was", "were", "be", "been", "being", + // Артикли + "a", "an", "the", + // Местоимения + "i", "you", "he", "she", "it", "we", "they", "me", "him", + "her", "us", "them", "my", "your", "his", "its", "our", "their", + "mine", "yours", "hers", "theirs", "myself", "yourself", "himself", + "herself", "itself", "ourselves", "yourselves", "themselves", "this", + "that", "these", "those", "who", "whom", "whose", "what", "which", + "some", "any", "none", "all", "many", "few", "several", + "everyone", "somebody", "anybody", "nobody", "everything", "anything", + "nothing", "each", "every", "either", "neither", + // Предлоги + "about", "above", "across", "after", "against", "along", "amid", "among", + "around", "as", "at", "before", "behind", "below", "beneath", "beside", + "besides", "between", "beyond", "but", "by", "despite", "down", "during", + "except", "for", "from", "in", "inside", "into", "like", "near", "of", "off", + "on", "onto", "out", "outside", "over", "past", "since", "through", "throughout", + "till", "to", "toward", "under", "underneath", "until", "up", "upon", "with", + "within", "without", + // Союзы + "and", "but", "or", "nor", "for", "yet", "so", "if", "because", "although", "though", + "since", "until", "unless", "while", "whereas", "when", "where", "before", "after", + // Междометия + "o","ah", "aha", "alas", "aw", "aye", "eh", "hmm", "huh", "hurrah", "no", "oh", "oops", + "ouch", "ow", "phew", "shh", "tsk", "ugh", "um", "wow", "yay", "yes", "yikes" + }; + + public WordFilter(IList? toAdd = null, IList? toExclude = null) + { + if (toAdd is not null) + { + foreach (var word in toAdd) + { + Add(word); + } + } + + if (toExclude is not null) + { + foreach (var word in toExclude) + { + Remove(word); + } + } + } + + public bool Add(string word) => bannedWords.Add(word); + + public bool Remove(string word) => bannedWords.Remove(word); + + public void Clear() => bannedWords.Clear(); + + public HashSet BannedWords => bannedWords; + public bool IsCorrectWord(string word) { - throw new NotImplementedException(); + return !bannedWords.Contains(word); } } } From d80488ea677a589eae272b018afccfc37a3352f2 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:37:20 +0500 Subject: [PATCH 09/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D1=8C=20Tag.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Она хранит в себе строку, которая будет написана внутри прямоугольника. --- TagCloud/Tag.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/TagCloud/Tag.cs b/TagCloud/Tag.cs index 5a0d4403..ee37ae00 100644 --- a/TagCloud/Tag.cs +++ b/TagCloud/Tag.cs @@ -2,7 +2,5 @@ namespace TagCloud { - // В дальнейшем буду работать с сущностью Tag, - // имеющей похожую структуру internal record Tag(string Text, Rectangle Rectangle); } From cd8e2012185f98a6bb54d33589708e16170fbe00 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:39:29 +0500 Subject: [PATCH 10/17] =?UTF-8?q?=D0=A1=D0=BE=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Содержимое произведения братьев Гримм "Белоснежка и семь гномов", которое я использовал при разработке. --- TagCloud/SnowWhiteContent.txt | 2941 +++++++++++++++++++++++++++++++++ 1 file changed, 2941 insertions(+) create mode 100644 TagCloud/SnowWhiteContent.txt diff --git a/TagCloud/SnowWhiteContent.txt b/TagCloud/SnowWhiteContent.txt new file mode 100644 index 00000000..23970728 --- /dev/null +++ b/TagCloud/SnowWhiteContent.txt @@ -0,0 +1,2941 @@ +Long +long +ago +in +the +winter +time +when +the +snowflakes +were +falling +like +little +white +feathers +from +the +sky +a +beautiful +Queen +sat +beside +her +window +which +was +framed +in +black +ebony +and +stitched +As +she +worked +she +looked +sometimes +at +the +falling +snow +and +so +it +happened +that +she +pricked +her +finger +with +her +needle +so +that +three +drops +of +blood +fell +upon +the +snow +How +pretty +the +red +blood +looked +upon +the +dazzling +white +The +Queen +said +to +herself +as +she +saw +it +Ah +me +If +only +I +had +a +dear +little +child +as +white +as +the +snow +as +rosy +as +the +blood +and +with +hair +as +black +as +the +ebony +window +frame +Soon +afterwards +a +little +daughter +came +to +her +who +was +white +as +snow +rosy +as +the +blood +and +whose +hair +was +as +black +as +ebony +so +she +was +called +Little +Snow +White +But +alas +When +the +little +one +came +the +good +Queen +died +A +year +passed +away +and +the +King +took +another +wife +She +was +very +beautiful +but +so +proud +and +haughty +that +she +could +not +bear +to +be +surpassed +in +beauty +by +anyone +She +possessed +a +wonderful +mirror +which +could +answer +her +when +she +stood +before +it +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +of +all +The +mirror +answered +Thou +O +Queen +art +the +fairest +of +all +and +the +Queen +was +contented +because +she +knew +the +mirror +could +speak +nothing +but +the +truth +But +as +time +passed +on +Little +Snow +White +grew +more +and +more +beautiful +until +when +she +was +seven +years +old +she +was +as +lovely +as +the +bright +day +and +still +more +lovely +than +the +Queen +herself +so +that +when +the +lady +one +day +asked +her +mirror +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +It +answered +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +The +Queen +was +horrified +and +from +that +moment +envy +and +pride +grew +in +her +heart +like +rank +weeds +until +one +day +she +called +a +huntsman +and +said +Take +the +child +away +into +the +woods +and +kill +her +for +I +can +no +longer +bear +the +sight +of +her +And +when +you +return +bring +with +you +her +heart +that +I +may +know +you +have +obeyed +my +will +The +huntsman +dared +not +disobey +so +he +led +Snow +White +out +into +the +woods +and +placed +an +arrow +in +his +bow +to +pierce +her +innocent +heart +but +the +little +maid +begged +him +to +spare +her +life +and +the +child’s +beauty +touched +his +heart +with +pity +so +that +he +bade +her +run +away +Then +as +a +young +wild +boar +came +rushing +by +he +killed +it +took +out +its +heart +and +carried +it +home +to +the +Queen +Poor +little +Snow +White +was +now +all +alone +in +the +wild +wood +and +so +frightened +was +she +that +she +trembled +at +every +leaf +that +rustled +So +she +began +to +run +and +ran +on +and +on +until +she +came +to +a +little +house +where +she +went +in +to +rest +In +the +little +house +everything +she +saw +was +tiny +but +more +dainty +and +clean +than +words +can +tell +Upon +a +white +covered +table +stood +seven +little +plates +and +upon +each +plate +lay +a +little +spoon +besides +which +there +were +seven +knives +and +forks +and +seven +little +goblets +Against +the +wall +and +side +by +side +stood +seven +little +beds +covered +with +snow +white +sheets +Snow +White +was +so +hungry +and +thirsty +that +she +took +a +little +food +from +each +of +the +seven +plates +and +drank +a +few +drops +of +wine +from +each +goblet +for +she +did +not +wish +to +take +everything +away +from +one +Then +because +she +was +so +tired +she +crept +into +one +bed +after +the +other +seeking +for +rest +but +one +was +too +long +another +too +short +and +so +on +until +she +came +to +the +seventh +which +suited +her +exactly +so +she +said +her +prayers +and +soon +fell +fast +asleep +When +night +fell +the +masters +of +the +little +house +came +home +They +were +seven +dwarfs +who +worked +with +a +pick +axe +and +spade +searching +for +cooper +and +gold +in +the +heart +of +the +mountains +They +lit +their +seven +candles +and +then +saw +that +someone +had +been +to +visit +them +The +first +said +Who +has +been +sitting +on +my +chair +The +second +said +Who +has +been +eating +from +my +plate +The +third +said +Who +has +taken +a +piece +of +my +bread +The +fourth +said +Who +has +taken +some +of +my +vegetables +The +fifth +Who +has +been +using +my +fork +The +sixth +Who +has +been +cutting +with +my +knife +The +seventh +Who +has +been +drinking +out +of +my +goblet +The +first +looked +round +and +saw +that +his +bed +was +rumpled +so +he +said +Who +has +been +getting +into +my +bed +Then +the +others +looked +round +and +each +one +cried +Someone +has +been +on +my +bed +too +But +the +seventh +saw +little +Snow +White +lying +asleep +in +his +bed +and +called +the +others +to +come +and +look +at +her +and +they +cried +aloud +with +surprise +and +fetched +their +seven +little +candles +so +that +they +might +see +her +the +better +and +they +were +so +pleased +with +her +beauty +that +they +let +her +sleep +on +all +night +When +the +sun +rose +Snow +White +awoke +and +oh +How +frightened +she +was +when +she +saw +the +seven +little +dwarfs +But +they +were +very +friendly +and +asked +what +her +name +was +My +name +is +Snow +White +she +answered +And +how +did +you +come +to +get +into +our +house +questioned +the +dwarfs +Then +she +told +them +how +her +cruel +step +mother +had +intended +her +to +be +killed +but +how +the +huntsman +had +spared +her +life +and +she +had +run +on +until +she +reached +the +little +house +And +the +dwarfs +said +If +you +will +take +care +of +our +house +cook +for +us +and +make +the +beds +wash +mend +and +knit +and +keep +everything +neat +and +clean +then +you +may +stay +with +us +altogether +and +you +shall +want +for +nothing +With +all +my +heart +answered +Snow +White +and +so +she +stayed +She +kept +the +house +neat +and +clean +for +the +dwarfs +who +went +off +early +in +the +morning +to +search +for +copper +and +gold +in +the +mountains +and +who +expected +their +meal +to +be +standing +ready +for +them +when +they +returned +at +night +All +day +long +Snow +White +was +alone +and +the +good +little +dwarfs +warned +her +to +be +careful +to +let +no +one +into +the +house +For +said +they +your +step +mother +will +soon +discover +that +you +are +living +here +The +Queen +believing +of +course +that +Snow +White +was +dead +and +that +therefore +she +was +again +the +most +beautiful +lady +in +the +land +went +to +her +mirror +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +Then +the +mirror +answered +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +Over +the +hills +and +far +away +She +dwells +with +seven +dwarfs +to +day +How +angry +she +was +for +she +knew +that +the +mirror +spoke +the +truth +and +that +the +huntsman +must +have +deceived +her +She +thought +and +thought +how +she +might +kill +Snow +White +for +she +knew +she +would +have +neither +rest +nor +peace +until +she +really +was +the +most +beautiful +lady +in +the +land +At +length +she +decided +what +to +do +She +painted +her +face +and +dressed +herself +like +an +old +peddler +woman +so +that +no +one +could +recognize +her +and +in +this +disguise +she +climbed +the +seven +mountains +that +lay +between +her +and +the +dwarfs’ +house +and +knocked +at +their +door +and +cried +Good +wares +to +sell +very +cheap +today +Snow +White +peeped +from +the +window +and +said +Good +day +good +wife +and +what +are +your +wares +All +sorts +of +pretty +things +my +dear +answered +the +woman +Silken +laces +of +every +colour +and +she +held +up +a +bright +coloured +one +made +of +plaited +silks +Surely +I +might +let +this +honest +old +woman +come +in +thought +Snow +White +and +unbolted +the +door +and +bought +the +pretty +lace +Dear +dear +what +a +figure +you +are +child +said +the +old +woman +come +let +me +lace +you +properly +for +once +Snow +White +had +no +suspicious +thoughts +so +she +placed +herself +in +front +of +the +old +woman +that +she +might +fasten +her +dress +with +the +new +silk +lace +But +in +less +than +no +time +the +wicked +creature +had +laced +her +so +tightly +that +she +could +not +breathe +but +fell +down +upon +the +ground +as +though +she +were +dead +Now +said +the +Queen +I +am +once +more +the +most +beautiful +lady +in +the +land +and +she +went +away +When +the +dwarfs +came +home +they +were +very +grieved +to +find +their +dear +little +Snow +White +lying +upon +the +ground +as +though +she +were +dead +They +lifted +her +gently +and +seeing +that +she +was +too +tightly +laced +they +cut +the +silken +cord +when +she +drew +a +long +breath +and +then +gradually +came +back +to +life +When +the +dwarfs +heard +all +that +had +happened +they +said +The +peddler +woman +was +certainly +the +wicked +Queen +Now +take +care +in +future +that +you +open +the +door +to +none +when +we +are +not +with +you +The +wicked +Queen +had +no +sooner +reached +home +than +she +went +to +her +mirror +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +And +the +mirror +answered +as +before +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +Over +the +hills +and +far +away +She +dwells +with +seven +dwarfs +to +day +The +blood +rushed +to +her +face +as +she +heard +these +words +for +she +knew +that +Snow +White +must +have +come +to +life +again +But +I +will +manage +to +put +an +end +to +her +yet +she +said +and +then +by +means +of +her +magic +she +made +a +poisonous +comb +Again +she +disguised +herself +climbed +the +seven +mountains +and +knocked +at +the +door +of +the +seven +dwarfs’ +cottage +crying +Good +wares +to +sell +very +cheap +today +Snow +White +looked +out +of +the +window +and +said +Go +away +good +woman +for +I +dare +not +let +you +in +Surely +you +can +look +at +my +goods +answered +the +woman +and +held +up +the +poisonous +comb +which +pleased +Snow +White +so +well +that +she +opened +the +door +and +bought +it +Come +let +me +comb +your +hair +in +the +newest +way +said +the +woman +and +the +poor +unsuspicious +child +let +her +have +her +way +but +no +sooner +did +the +comb +touch +her +hair +than +the +poison +began +to +work +and +she +fell +fainting +to +the +ground +There +you +model +of +beauty +said +the +wicked +woman +as +she +went +away +you +are +done +for +at +last +But +fortunately +it +was +almost +time +for +the +dwarfs +to +come +home +and +as +soon +as +they +came +in +and +found +Snow +White +lying +upon +the +ground +they +guessed +that +her +wicked +step +mother +had +been +there +again +and +set +to +work +to +find +out +what +was +wrong +They +soon +saw +the +poisonous +comb +and +drew +it +out +and +almost +immediately +Snow +White +began +to +recover +and +told +them +what +had +happened +Once +more +they +warned +her +to +be +on +her +guard +and +to +open +the +door +to +no +one +When +the +Queen +reached +home +she +went +straight +to +the +mirror +and +said +Mirror +mirror +on +the +wall +Who +is +the +fairest +fair +of +all +And +the +mirror +answered +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +Over +the +hills +and +far +away +She +dwells +with +seven +dwarfs +to +day +When +the +Queen +heard +these +words +she +shook +with +rage +Snow +White +shall +die +she +cried +even +if +it +costs +me +my +own +life +to +manage +it +She +went +into +a +secret +chamber +where +no +one +else +ever +entered +and +there +she +made +a +poisonous +apple +and +then +she +painted +her +face +and +disguised +herself +as +a +peasant +woman +and +climbed +the +seven +mountains +and +went +to +the +dwarfs’ +house +She +knocked +at +the +door +Snow +White +put +her +head +out +of +the +window +and +said +I +must +not +let +anyone +in +the +seven +dwarfs +have +forbidden +me +to +do +so +It’s +all +the +same +to +me +answered +the +peasant +woman +I +shall +soon +get +rid +of +these +fine +apples +But +before +I +go +I’ll +make +you +a +present +of +one +Oh +No +said +Snow +White +for +I +must +not +take +it +Surely +you +are +not +afraid +of +poison +said +the +woman +See +I +will +cut +one +in +two +the +rosy +cheek +you +shall +take +and +the +white +cheek +I +will +eat +myself +Now +the +apple +had +been +so +cleverly +made +that +only +the +rose +cheeked +side +contained +the +poison +Snow +White +longed +for +the +delicious +looking +fruit +and +when +she +saw +that +the +woman +ate +half +of +it +she +thought +there +could +be +no +danger +and +stretched +out +her +hand +and +took +the +other +part +But +no +sooner +had +she +tasted +it +than +she +fell +down +dead +The +wicked +Queen +laughed +aloud +with +joy +as +she +gazed +at +her +White +as +snow +red +as +blood +black +as +ebony +she +said +this +time +the +dwarfs +cannot +awaken +you +And +she +went +straight +home +and +asked +her +mirror +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +And +at +length +it +answered +Thou +O +Queen +art +fairest +of +all +So +her +envious +heart +had +peace +at +least +so +much +peace +as +an +envious +heart +can +have +When +the +little +dwarfs +came +home +at +night +they +found +Snow +White +lying +upon +the +ground +No +breath +came +from +her +parted +lips +for +she +was +dead +They +lifted +her +tenderly +and +sought +for +some +poisonous +object +which +might +have +caused +the +mischief +unlaced +her +frock +combed +her +hair +and +washed +her +with +wine +and +water +but +all +in +vain +dead +she +was +and +dead +she +remained +They +laid +her +upon +a +bier +and +all +seven +of +them +sat +round +about +it +and +wept +as +though +their +hearts +would +break +for +three +whole +days +When +the +time +came +that +she +should +be +laid +in +the +ground +they +could +not +bear +to +part +from +her +Her +pretty +cheeks +were +still +rosy +red +and +she +looked +just +as +though +she +were +still +living +We +cannot +hide +her +away +in +the +dark +earth +said +the +dwarfs +and +so +they +made +a +transparent +coffin +of +shining +glass +and +laid +her +in +it +and +wrote +her +name +upon +it +in +letters +of +gold +also +they +wrote +that +she +was +a +King’s +daughter +Then +they +placed +the +coffin +upon +the +mountain +top +and +took +it +in +turns +to +watch +beside +it +And +all +the +animals +came +and +wept +for +Snow +White +first +an +owl +then +a +raven +and +then +a +little +dove +For +a +long +long +time +little +Snow +White +lay +in +the +coffin +but +her +form +did +not +wither +she +only +looked +as +though +she +slept +for +she +was +still +as +white +as +snow +as +red +as +blood +and +as +black +as +ebony +It +chanced +that +a +King’s +son +came +into +the +wood +and +went +to +the +dwarfs’ +house +meaning +to +spend +the +night +there +He +saw +the +coffin +upon +the +mountain +top +with +little +Snow +White +lying +within +it +and +he +read +the +words +that +were +written +upon +it +in +letters +of +gold +And +he +said +to +the +dwarfs +If +you +will +but +let +me +have +the +coffin +you +may +ask +of +me +what +you +will +and +I +will +give +it +to +you +But +the +dwarfs +answered +We +would +not +sell +it +for +all +the +gold +in +the +world +Then +said +the +Prince +Let +me +have +it +as +a +gift +I +pray +you +for +I +cannot +live +without +seeing +little +Snow +White +and +I +will +prize +your +gift +as +the +dearest +of +my +possessions +The +good +little +dwarfs +pitied +him +when +they +heard +these +words +and +so +gave +him +the +coffin +The +King’s +son +then +bade +his +servants +place +it +upon +their +shoulders +and +carry +it +away +but +as +they +went +they +stumbled +over +the +stump +of +a +tree +and +the +violent +shaking +shook +the +piece +of +poisonous +apple +which +had +lodged +in +Snow +White’s +throat +out +again +so +that +she +opened +her +eyes +raised +the +lid +of +the +coffin +and +sat +up +alive +once +more +Where +am +I +she +cried +and +the +happy +Prince +answered +Thou +art +with +me +dearest +Then +he +told +her +all +that +had +happened +and +how +he +loved +her +better +than +the +whole +world +and +begged +her +to +go +with +him +to +his +father’s +palace +and +be +his +wife +Snow +White +consented +and +went +with +him +and +the +wedding +was +celebrated +with +great +splendour +and +magnificence +Little +Snow +White’s +wicked +step +mother +was +bidden +to +the +feast +and +when +she +had +arrayed +herself +in +her +most +beautiful +garments +she +stood +before +her +mirror +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +And +the +mirror +answered +O +Lady +Queen +though +fair +ye +be +The +young +Queen +is +fairer +to +see +Oh +How +angry +the +wicked +woman +was +then +and +so +terrified +too +that +she +scarcely +knew +what +to +do +At +first +she +thought +she +would +not +go +to +the +wedding +at +all +but +then +she +felt +that +she +could +not +rest +until +she +had +seen +the +young +Queen +No +sooner +did +she +enter +the +palace +than +she +recognized +little +Snow +White +and +could +not +move +for +terror +Then +a +pair +of +iron +shoes +was +brought +into +the +room +and +set +before +her +and +these +she +was +forced +to +put +on +and +to +dance +in +them +until +she +could +dance +no +longer +but +fell +down +dead +and +that +was +the +end +of +her \ No newline at end of file From 7013070aac82fc9ac58a3d02cdf7924e0126a8b2 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:41:14 +0500 Subject: [PATCH 11/17] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=B4=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Использование контейнера зависимостей Autofac. 2. Использование CommandLine для парсинга аргументов командной строки. --- TagCloud/CommandLineOptions.cs | 72 +++++++++++++++++++++ TagCloud/DIContainer.cs | 105 +++++++++++++++++++++++++++++++ TagCloud/GlobalSuppressions.cs | 15 +++++ TagCloud/Parsers/BoolParser.cs | 14 +++++ TagCloud/Parsers/ColorParser.cs | 17 +++++ TagCloud/Parsers/FontParser.cs | 18 ++++++ TagCloud/Parsers/SizeParser.cs | 21 +++++++ TagCloud/Program.cs | 107 ++++++-------------------------- TagCloud/ProgramExecutor.cs | 35 +++++++++++ TagCloud/TagCloud.csproj | 3 + 10 files changed, 319 insertions(+), 88 deletions(-) create mode 100644 TagCloud/CommandLineOptions.cs create mode 100644 TagCloud/DIContainer.cs create mode 100644 TagCloud/GlobalSuppressions.cs create mode 100644 TagCloud/Parsers/BoolParser.cs create mode 100644 TagCloud/Parsers/ColorParser.cs create mode 100644 TagCloud/Parsers/FontParser.cs create mode 100644 TagCloud/Parsers/SizeParser.cs create mode 100644 TagCloud/ProgramExecutor.cs diff --git a/TagCloud/CommandLineOptions.cs b/TagCloud/CommandLineOptions.cs new file mode 100644 index 00000000..44706271 --- /dev/null +++ b/TagCloud/CommandLineOptions.cs @@ -0,0 +1,72 @@ +using CommandLine; + +namespace TagCloud +{ + // 1. Нужно добавить опцию для указания имени файла, + // который содержит слова, для иссключения из фильтра скучных слов + // 2. Нужно добавить опцию для указания имени файла, + // который содержит слова, для добавления в фильтр скучных слов + public class CommandLineOptions + { + [Option( + "backgroundColor", + Required = false, + HelpText = "Цвет заднего фона изображения, например White.")] + public string BackgroundColor { get; set; } = "White"; + + [Option( + "textColor", + Required = false, + HelpText = "Цвет текста на изображении, например Black.")] + public string TextColor { get; set; } = "Black"; + + [Option( + "font", + Required = false, + HelpText = "Шрифт текста на изображении, например Arial.")] + public string Font { get; set; } = "Arial"; + + [Option( + "nonSorted", + Required = false, + HelpText = "Отключение сортировки слов, например False")] + public string IsSorted { get; set; } = true.ToString(); + + [Option( + "size", + Required = false, + HelpText = "Размер изображения в формате ШИРИНА:ВЫСОТА, например 5000:5000.")] + public string ImageSize { get; set; } = "5000:5000"; + + [Option( + "maxRectangleWidth", + Required = false, + HelpText = "Максимальная ширина прямоугольника.")] + public int MaxRectangleWidth { get; set; } = 500; + + [Option( + "maxRectangleHeight", + Required = false, + HelpText = "Максимальная высота прямоугольника.")] + public int MaxRectangleHeight { get; set; } = 200; + + [Option( + "imageFile", + Required = false, + HelpText = "Имя выходного файла изображения.")] + public string ImageFileName { get; set; } = "Result"; + + [Option( + "dataFile", + Required = true, + HelpText = "Имя файла с исходными данными.")] + public required string DataFileName { get; set; } + + [Option( + "resultFormat", + Required = false, + HelpText = "Формат создаваемого изображение, например png.")] + public string ResultFormat { get; set; } = "png"; + } + +} \ No newline at end of file diff --git a/TagCloud/DIContainer.cs b/TagCloud/DIContainer.cs new file mode 100644 index 00000000..13dbbb6e --- /dev/null +++ b/TagCloud/DIContainer.cs @@ -0,0 +1,105 @@ +using Autofac; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.CloudLayouters; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; +using TagCloud.Parsers; + +namespace TagCloud +{ + public static class DIContainer + { + // 1. Разбить содержимое этого метода на отдельны части + // 2. Добавить проверку корректностей значений: + // - options.ImageSize; + // - options.MaxRectangleWidth; + // - options.MaxRectangleHeight; + public static IContainer ConfigureContainer(CommandLineOptions options) + { + var builder = new ContainerBuilder(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + var imageSize = SizeParser.ParseImageSize(options.ImageSize); + var backgroundColor = ColorParser.ParseColor(options.BackgroundColor); + var textColor = ColorParser.ParseColor(options.TextColor); + var font = FontParser.ParseFont(options.Font); + builder.RegisterType() + .As() + .WithParameter("imageSize", imageSize) + .WithParameter("backgroundColor", backgroundColor) + .WithParameter("textColor", textColor) + .WithParameter("fontName", font) + .SingleInstance(); + + var isSorted = BoolParser.ParseIsSorted(options.IsSorted); + builder.Register((c, p) => + { + var wordReader = c.Resolve(); + var wordCounter = c.Resolve(); + var normalizer = c.Resolve(); + var wordFilter = c.Resolve(); + + foreach (var word in wordReader.ReadByLines(options.DataFileName)) + { + var wordInLowerCase = word.ToLower(); + if (!wordFilter.IsCorrectWord(wordInLowerCase)) + { + continue; + } + wordCounter.AddWord(wordInLowerCase); + } + + var normalizedValues = normalizer.Normalize(wordCounter.Values); + return new NormalizedFrequencyBasedCloudLayouterWorker( + options.MaxRectangleWidth, + options.MaxRectangleHeight, + normalizedValues, + isSorted); + }).As().SingleInstance(); + + builder.RegisterType() + .WithParameter("size", imageSize) + .WithParameter("maxRectangleWidth", options.MaxRectangleWidth) + .WithParameter("maxRectangleHeight", options.MaxRectangleHeight) + .WithParameter("imageFileName", options.ImageFileName) + .WithParameter("dataFileName", options.DataFileName) + .SingleInstance(); + + return builder.Build(); + } + } +} diff --git a/TagCloud/GlobalSuppressions.cs b/TagCloud/GlobalSuppressions.cs new file mode 100644 index 00000000..75089be8 --- /dev/null +++ b/TagCloud/GlobalSuppressions.cs @@ -0,0 +1,15 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + + +// Так делать явно плохо +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.DrawText(System.Drawing.Graphics,System.Drawing.Rectangle,System.String)")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.Draw(System.Collections.Generic.IList{TagCloud.Tag})~System.Drawing.Bitmap")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.FindFittingFontSize(System.Drawing.Graphics,System.String,System.Drawing.Rectangle)~System.Int32")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:Program.ParseFont(System.String)~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ImageSavers.ImageSaver.SaveFile(System.Drawing.Bitmap,System.String,System.String)")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~System.String")] diff --git a/TagCloud/Parsers/BoolParser.cs b/TagCloud/Parsers/BoolParser.cs new file mode 100644 index 00000000..82b78e87 --- /dev/null +++ b/TagCloud/Parsers/BoolParser.cs @@ -0,0 +1,14 @@ +namespace TagCloud.Parsers +{ + internal static class BoolParser + { + public static bool ParseIsSorted(string value) + { + if (value == false.ToString() || value == true.ToString()) + { + return value == true.ToString(); + } + throw new ArgumentException($"Неизвестный параметр сортировки {value}"); + } + } +} diff --git a/TagCloud/Parsers/ColorParser.cs b/TagCloud/Parsers/ColorParser.cs new file mode 100644 index 00000000..0d7ed14e --- /dev/null +++ b/TagCloud/Parsers/ColorParser.cs @@ -0,0 +1,17 @@ +using System.Drawing; + +namespace TagCloud.Parsers +{ + internal static class ColorParser + { + public static Color ParseColor(string color) + { + var result = Color.FromName(color); + if (!result.IsKnownColor) + { + throw new ArgumentException($"Неизвестный цвет {color}"); + } + return result; + } + } +} diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs new file mode 100644 index 00000000..46c97ada --- /dev/null +++ b/TagCloud/Parsers/FontParser.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace TagCloud.Parsers +{ + internal static class FontParser + { + public static string ParseFont(string font) + { + if (!FontFamily.Families.Any( + x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) + { + throw new ArgumentException($"Неизвестный шрифт {font}"); + } + return font; + } + + } +} diff --git a/TagCloud/Parsers/SizeParser.cs b/TagCloud/Parsers/SizeParser.cs new file mode 100644 index 00000000..1ad249b3 --- /dev/null +++ b/TagCloud/Parsers/SizeParser.cs @@ -0,0 +1,21 @@ +using System.Drawing; + +namespace TagCloud.Parsers +{ + internal static class SizeParser + { + public static Size ParseImageSize(string size) + { + var dimensions = size.Split(':'); + if (dimensions.Length != 2 + || !int.TryParse(dimensions[0], out var width) + || !int.TryParse(dimensions[1], out var height)) + { + throw new ArgumentException( + $"Некорректный формат размера изображения: {size}." + + $" Используйте формат Ширина:Высота, например 5000:5000."); + } + return new Size(width, height); + } + } +} diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 61b80b47..47680d12 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -1,106 +1,37 @@ -using System.Drawing; +using Autofac; +using CommandLine; using System.Runtime.CompilerServices; -using TagCloud.CloudLayouterPainters; -using TagCloud.CloudLayouters.CircularCloudLayouter; -using TagCloud.CloudLayouterWorkers; -using TagCloud.ImageSavers; -using TagCloud.Normalizers; -using TagCloud.WordCounters; -using TagCloud.WordFilters; -using TagCloud.WordReaders; [assembly: InternalsVisibleTo("TagCloud.Tests")] namespace TagCloud { internal class Program { - static void Main(string[] args) - { - StartOldVariant(); - } + private static IContainer container; - // Пример работы со старого задания TagCloud - private static void StartOldVariant() + static void Main(string[] args) { - var center = new Point(400, 400); - var minRectangleWidth = 30; - var maxRectangleWidth = 70; - var minRectangleHeight = 20; - var maxRectangleHeight = 50; - var rectanglesCount = 1000; - var imageFileName = "Result.png"; + var parserResult = Parser.Default.ParseArguments(args); - var rectangles = new List(); - var circularCloudLayouter = new CircularCloudLayouter(center); - - var randomWorker = new RandomCloudLayouterWorker( - minRectangleWidth, - maxRectangleWidth, - minRectangleHeight, - maxRectangleHeight); - - foreach (var rectangleSize in randomWorker.GetNextRectangleSize(rectanglesCount)) + parserResult.WithParsed(options => { - rectangles.Add(circularCloudLayouter.PutNextRectangle(rectangleSize)); - } + container = DIContainer.ConfigureContainer(options); + Run(options.ResultFormat); + }); - var painter = new CloudLayouterPainter(); - var imageSaver = new ImageSaver(); - imageSaver.SaveFile(painter.Draw(rectangles), imageFileName); + parserResult.WithNotParsed(errors => + { + Console.WriteLine("Ошибка парсинга аргументов:"); + foreach (var error in errors) + Console.WriteLine(error.ToString()); + }); } - // Пример работы с новым вариантом - private static void StartNewVariant() + private static void Run(string resultFormat) { - var center = new Point(400, 400); - var minRectangleWidth = 30; - var maxRectangleWidth = 70; - var minRectangleHeight = 20; - var maxRectangleHeight = 50; - - var imageFileName = "Result.png"; - var dataFileName = "Values.txt"; - - var wordReader = new WordReader(); - var wordFilter = new WordFilter(); - var wordCounter = new WordCounter(); - - foreach (var word in wordReader.ReadByLines(dataFileName)) - { - if (!wordFilter.IsCorrectWord(word)) - { - continue; - } - wordCounter.AddWord(word); - } - - var normalizer = new Normalizer(); - var normalizedWordCounts = normalizer.Normalize(wordCounter.Counts); - - var circularCloudLayouter = new CircularCloudLayouter(center); - var frequencyBasedCloudLayouterWorker = - new FrequencyBasedCloudLayouterWorker( - minRectangleWidth, - maxRectangleWidth, - minRectangleHeight, - maxRectangleHeight, - normalizedWordCounts); - - // Начиная с этого места, - // в дальнейшем нужно будет использовать - // Tag вместо Rectangle. - // В черновом варианте не переделывал, - // что бы не ломать старые написанные тесты. - var rectangles = new List(); - foreach (var rectangleSize in frequencyBasedCloudLayouterWorker - .GetNextRectangleSize(wordCounter.Counts.Count)) - { - rectangles.Add(circularCloudLayouter.PutNextRectangle(rectangleSize)); - } - - var painter = new CloudLayouterPainter(); - var imageSaver = new ImageSaver(); - imageSaver.SaveFile(painter.Draw(rectangles), imageFileName); + using var scope = container.BeginLifetimeScope(); + var program = scope.Resolve(); + program.Execute(resultFormat); } } } diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs new file mode 100644 index 00000000..c50e0d2e --- /dev/null +++ b/TagCloud/ProgramExecutor.cs @@ -0,0 +1,35 @@ +using System.Drawing; +using TagCloud.CloudLayouters; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud +{ + internal class ProgramExecutor( + string imageFileName, + IWordCounter wordCounter, + INormalizer normalizer, + ICloudLayouter layouter, + ICloudLayouterPainter painter, + ICloudLayouterWorker worker, + IImageSaver imageSaver) + { + public void Execute(string resultFormat) + { + var normalizedWordWeights = normalizer.Normalize(wordCounter.Values); + + var tags = new List(); + foreach (var rectangleProperty in worker.GetNextRectangleProperties()) + { + tags.Add(new Tag(rectangleProperty.word, layouter.PutNextRectangle(rectangleProperty.size))); + } + + imageSaver.SaveFile(painter.Draw(tags), imageFileName, resultFormat); + } + } +} diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj index 71159cfa..113b51b7 100644 --- a/TagCloud/TagCloud.csproj +++ b/TagCloud/TagCloud.csproj @@ -8,6 +8,9 @@ + + + From 3f63588c7c595c85397d2090fc195e11290c6f37 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Sun, 29 Dec 2024 21:41:33 +0500 Subject: [PATCH 12/17] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainterTest.cs | 30 +++++++++ ...rcularCloudLayouterMainRequirementsTest.cs | 34 ++++++---- .../CircularCloudLayouterTest.cs | 2 +- ...edFrequencyBasedCloudLayouterWorkerTest.cs | 60 +++++++++++++++++ .../RandomCloudLayouterWorkerTest.cs | 4 +- .../Extensions/RectangleExtensions.cs | 10 ++- TagCloud.Tests/GlobalSuppressions.cs | 10 +++ .../ImageSaversTests/ImageSaverTest.cs | 15 +++-- TagCloud.Tests/MainTest.cs | 64 +++++++++++++++++++ .../NormalizersTest/NormalizerTest.cs | 49 ++++++++++++++ .../WordCountersTests/WordCounterTest.cs | 2 +- .../WordFiltersTests/BannedWordLists.cs | 60 +++++++++++++++++ .../WordFilterChangeBannedWordsTest.cs | 41 ++++++++++++ .../WordFilterDefaultBannedWordsTest.cs | 59 +++++++++++++++++ .../WordReadersTests/WordReaderTest.cs | 15 +++-- 15 files changed, 425 insertions(+), 30 deletions(-) create mode 100644 TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs create mode 100644 TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs create mode 100644 TagCloud.Tests/GlobalSuppressions.cs create mode 100644 TagCloud.Tests/MainTest.cs create mode 100644 TagCloud.Tests/NormalizersTest/NormalizerTest.cs create mode 100644 TagCloud.Tests/WordFiltersTests/BannedWordLists.cs create mode 100644 TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs create mode 100644 TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs new file mode 100644 index 00000000..549ca468 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using TagCloud.CloudLayouterPainters; + +namespace TagCloud.Tests.CloudLayouterPaintersTest +{ + internal class CloudLayouterPainterTest + { + private CloudLayouterPainter painter; + + [SetUp] + public void SetUp() + { + painter = new CloudLayouterPainter(new Size(1, 1)); + } + + [Test] + public void Draw_ThrowsArgumentException_WithEmptyTags() + { + var painter = new CloudLayouterPainter(new Size(1, 1)); + Assert.Throws(() => painter.Draw(new List())); + } + + [Test] + public void Draw_ThrowsArgumentNullException_WithTagsAsNull() + { + var painter = new CloudLayouterPainter(new Size(1, 1)); + Assert.Throws(() => painter.Draw(null!)); + } + } +} diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs index 89695829..cc2f2dfc 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -11,12 +11,14 @@ namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests [TestFixture] internal class CircularCloudLayouterMainRequirementsTest { - private Point center; - private List rectangles; + private Point center = new Point(); + private Rectangle[] rectangles; + private List tags; private readonly string failedTestsDirectory = "FailedTest"; private readonly ImageSaver imageSaver = new ImageSaver(); - private readonly CloudLayouterPainter cloudLayouterPainter = new CloudLayouterPainter(); + private readonly CloudLayouterPainter cloudLayouterPainter + = new CloudLayouterPainter(new Size(5000, 5000)); [OneTimeSetUp] public void Init() @@ -27,32 +29,37 @@ public void Init() [SetUp] public void SetUp() { - center = new Point(400, 400); var minRectangleWidth = 30; var maxRectangleWidth = 70; var minRectangleHeight = 20; var maxRectangleHeight = 50; var rectanglesCount = 1000; - rectangles = new List(); - var circularCloudLayouter = new CircularCloudLayouter(center); + tags = new List(); + var circularCloudLayouter = new CircularCloudLayouter(); var randomWorker = new RandomCloudLayouterWorker( minRectangleWidth, maxRectangleWidth, minRectangleHeight, maxRectangleHeight); - foreach (var rectangleSize in randomWorker.GetNextRectangleSize(rectanglesCount)) + foreach (var rectangleProperty in randomWorker + .GetNextRectangleProperties().Take(rectanglesCount)) { - rectangles.Add(circularCloudLayouter.PutNextRectangle(rectangleSize)); + tags.Add( + new Tag( + rectangleProperty.word, + circularCloudLayouter.PutNextRectangle(rectangleProperty.size))); } + rectangles = tags.Select(x => x.Rectangle).ToArray(); } [TestCase(0.7, 1000)] [Repeat(10)] public void ShouldPlaceRectanglesInCircle(double expectedCoverageRatio, int gridSize) { - var maxRadius = rectangles.Max(r => r.GetMaxDistanceFromPointToRectangleAngles(center)); + var maxRadius = rectangles.Max( + x => x.GetMaxDistanceFromPointToRectangleAngles(center)); var step = 2 * maxRadius / gridSize; var occupancyGrid = GetOccupancyGrid(gridSize, maxRadius, step); @@ -79,9 +86,9 @@ public void ShouldPlaceCenterOfMassOfRectanglesNearCenter(int tolerance) [Repeat(10)] public void ShouldPlaceRectanglesWithoutOverlap() { - for (var i = 0; i < rectangles.Count; i++) + for (var i = 0; i < rectangles.Length; i++) { - for (var j = i + 1; j < rectangles.Count; j++) + for (var j = i + 1; j < rectangles.Length; j++) { Assert.That( rectangles[i].IntersectsWith(rectangles[j]) == false, @@ -102,7 +109,7 @@ public void Cleanup() var name = $"{TestContext.CurrentContext.Test.Name}.png"; var path = Path.Combine(failedTestsDirectory, name); - imageSaver.SaveFile(cloudLayouterPainter.Draw(rectangles), path); + imageSaver.SaveFile(cloudLayouterPainter.Draw(tags), path); Console.WriteLine($"Tag cloud visualization saved to file {path}"); } @@ -123,7 +130,8 @@ public void OneTimeCleanup() double step) { var start = (int)((rectangleStartValue - center.X + maxRadius) / step); - var end = (int)((rectangleStartValue + rectangleCorrespondingSize - center.X + maxRadius) / step); + var end = (int)((rectangleStartValue + + rectangleCorrespondingSize - center.X + maxRadius) / step); return (start, end); } diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs index 2ce625f2..8a5ed0e7 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs @@ -16,7 +16,7 @@ public void PutNextRectangle_ThrowsArgumentException_OnAnyNegativeOrZeroSize( { var size = new Size(width, height); Assert.Throws( - () => new CircularCloudLayouter(new Point()).PutNextRectangle(size)); + () => new CircularCloudLayouter().PutNextRectangle(size)); } } } diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs new file mode 100644 index 00000000..a6d75e2f --- /dev/null +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs @@ -0,0 +1,60 @@ +using FluentAssertions; +using System.Drawing; +using TagCloud.CloudLayouterWorkers; + +namespace TagCloud.Tests.CloudLayouterWorkersTests +{ + internal class NormalizedFrequencyBasedCloudLayouterWorkerTest + { + private readonly Dictionary normalizedValues + = new Dictionary + { + { "three", 0.625 }, + { "one", 0.25 }, + { "two", 0.2917 }, + { "four", 1.0 }, + }; + + [TestCase(0, 100)] + [TestCase(-1, 100)] + [TestCase(100, 0)] + [TestCase(100, -1)] + public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + int width, + int height) + { + Assert.Throws( + () => new NormalizedFrequencyBasedCloudLayouterWorker(width, height, normalizedValues)); + } + + [TestCase(100, 25, false)] + [TestCase(100, 25, true)] + public void GetNextRectangleSize_WorksCorrectly(int width, int height, bool isSortedOrder) + { + var index = 0; + string[]? keys = null; + if (isSortedOrder) + { + keys = normalizedValues.OrderByDescending(x => x.Value).Select(x => x.Key).ToArray(); + } + else + { + keys = normalizedValues.Keys.ToArray(); + } + + var worker = new NormalizedFrequencyBasedCloudLayouterWorker( + width, + height, + normalizedValues, + isSortedOrder); + foreach (var rectangleSize in worker + .GetNextRectangleProperties()) + { + var currentValue = normalizedValues[keys[index]]; + var expected = new Size((int)(currentValue * width), (int)(currentValue * height)); + index += 1; + rectangleSize.size.Should().BeEquivalentTo(expected); + } + } + } +} diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs index f5ea045a..81f71e26 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs @@ -9,7 +9,9 @@ internal class CircularCloudLayouterWorkerTests [TestCase(-1, 100)] [TestCase(100, 0)] [TestCase(100, -1)] - public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize(int width, int height) + public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + int width, + int height) { Assert.Throws( () => new RandomCloudLayouterWorker(width, width, height, height)); diff --git a/TagCloud.Tests/Extensions/RectangleExtensions.cs b/TagCloud.Tests/Extensions/RectangleExtensions.cs index 1b567b7b..d4c8aa55 100644 --- a/TagCloud.Tests/Extensions/RectangleExtensions.cs +++ b/TagCloud.Tests/Extensions/RectangleExtensions.cs @@ -4,12 +4,16 @@ namespace TagCloud.Tests.Extensions { internal static class RectangleExtensions { - public static double GetMaxDistanceFromPointToRectangleAngles(this Rectangle rectangle, Point point) + public static double GetMaxDistanceFromPointToRectangleAngles( + this Rectangle rectangle, + Point point) { var dx = Math.Max( - Math.Abs(rectangle.X - point.X), Math.Abs(rectangle.X + rectangle.Width - point.X)); + Math.Abs(rectangle.X - point.X), + Math.Abs(rectangle.X + rectangle.Width - point.X)); var dy = Math.Max( - Math.Abs(rectangle.Y - point.Y), Math.Abs(rectangle.Y + rectangle.Height - point.Y)); + Math.Abs(rectangle.Y - point.Y), + Math.Abs(rectangle.Y + rectangle.Height - point.Y)); return Math.Sqrt(dx * dx + dy * dy); } } diff --git a/TagCloud.Tests/GlobalSuppressions.cs b/TagCloud.Tests/GlobalSuppressions.cs new file mode 100644 index 00000000..cdd580ff --- /dev/null +++ b/TagCloud.Tests/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +// Так делать явно плохо +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_SavesFile(System.String,System.String)~System.Boolean")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_ThrowsArgumentException_WithInvalidFilename(System.String)")] diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index 983146b0..5a4b1bb5 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -21,11 +21,11 @@ public void SetUp() imageSaver = new ImageSaver(); } - [TestCase("Test.png")] + [TestCase("Test")] public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) { var path = Path.Combine(directoryPath, filename); - Assert.Throws(() => imageSaver.SaveFile(null, path)); + Assert.Throws(() => imageSaver.SaveFile(null!, path)); } [TestCase(null)] @@ -34,18 +34,19 @@ public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) public void SaveFile_ThrowsArgumentException_WithInvalidFilename(string? filename) { var dummyImage = new Bitmap(1, 1); - Assert.Throws(() => imageSaver.SaveFile(dummyImage, filename)); + Assert.Throws(() => imageSaver.SaveFile(dummyImage, filename!)); } - [TestCase("Test.png", ExpectedResult = true)] - public bool SaveFile_SavesFile(string filename) + [TestCase("Test", "png", ExpectedResult = true)] + [TestCase("Test", "bmp", ExpectedResult = true)] + public bool SaveFile_SavesFile(string filename, string format) { var dummyImage = new Bitmap(1, 1); var path = Path.Combine(directoryPath, filename); File.Delete(path); - imageSaver.SaveFile(dummyImage, path); - return File.Exists(path); + imageSaver.SaveFile(dummyImage, path, format); + return File.Exists($"{path}.{format}"); } diff --git a/TagCloud.Tests/MainTest.cs b/TagCloud.Tests/MainTest.cs new file mode 100644 index 00000000..76bf6026 --- /dev/null +++ b/TagCloud.Tests/MainTest.cs @@ -0,0 +1,64 @@ +using Autofac; +using FluentAssertions; +namespace TagCloud.Tests +{ + [TestFixture] + internal class MainTest + { + private static string directoryPath = "TempFilesForMainTest"; + private static readonly string dataFile = Path.Combine(directoryPath, "TestData.txt"); + private readonly string imageFile = Path.Combine(directoryPath, "Test"); + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(directoryPath); + File.WriteAllLines(dataFile, new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }); + } + + [TestCase("bmp")] + public void Program_ExecutesSuccessfully_WithValidArguments(string format) + { + var options = new CommandLineOptions + { + BackgroundColor = "Red", + TextColor = "Blue", + Font = "Calibri", + IsSorted = true.ToString(), + ImageSize = "1000:1000", + MaxRectangleHeight = 100, + MaxRectangleWidth = 200, + ImageFileName = imageFile, + DataFileName = dataFile, + ResultFormat = format + }; + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + + Assert.DoesNotThrow(() => executor.Execute(format)); + + File.Exists($"{imageFile}.{format}").Should().BeTrue(); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs new file mode 100644 index 00000000..92121b77 --- /dev/null +++ b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using TagCloud.Normalizers; + +namespace TagCloud.Tests.WordCountersTests +{ + [TestFixture] + internal class NormalizerTest + { + private readonly Normalizer normalizer = new Normalizer(); + private readonly Dictionary values = new Dictionary + { + { "one", 14 }, + { "two", 15 }, + { "three", 23 }, + { "four", 32 }, + }; + private readonly Dictionary expectedResult = new Dictionary + { + { "one", 0.25 }, + { "two",0.29166666666666669 }, + { "three", 0.625 }, + { "four", 1.0 }, + + }; + private readonly int defaultDecimalPlaces = 4; + private readonly double defaultMinCoefficient = 0.25; + + [TestCase(-0.1)] + public void Normalize_ThrowsArgumentException_WithMinCoefficientLessThanZero( + double minCoefficient) + { + Assert.Throws(() + => normalizer.Normalize(values, minCoefficient, defaultDecimalPlaces)); + } + + [TestCase(0.25, 4)] + [TestCase(0.25, 2)] + public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPlaces) + { + foreach (var pair in expectedResult) + { + expectedResult[pair.Key] = Math.Round(pair.Value, decimalPlaces); + } + var actual = normalizer.Normalize(values, minCoefficient, decimalPlaces); + + actual.Should().BeEquivalentTo(expectedResult); + } + } +} diff --git a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs index 1e55aabf..5a2a698f 100644 --- a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs +++ b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs @@ -40,7 +40,7 @@ public void WordCounter_CountsCorrect() { wordCounter.AddWord(value); } - wordCounter.Counts.Should().BeEquivalentTo(expected); + wordCounter.Values.Should().BeEquivalentTo(expected); } } } diff --git a/TagCloud.Tests/WordFiltersTests/BannedWordLists.cs b/TagCloud.Tests/WordFiltersTests/BannedWordLists.cs new file mode 100644 index 00000000..5dea1302 --- /dev/null +++ b/TagCloud.Tests/WordFiltersTests/BannedWordLists.cs @@ -0,0 +1,60 @@ +namespace TagCloud.Tests.WordFiltersTests +{ + internal static class BannedWordLists + { + public static string[] CustomBans = new string[] + { + "not", "also", "how", "let" + }; + + public static string[] ToHaveForms = new string[] + { + "have", "has", "had", "having", + }; + + public static string[] ToBeForms = new string[] + { + "am", "is", "are", "was", "were", "be", "been", "being", + }; + + public static string[] Articles = new string[] + { + "a", "an", "the" + }; + + public static string[] Pronouns => new string[] + { + "i", "you", "he", "she", "it", "we", "they", "me", "him", + "her", "us", "them", "my", "your", "his", "its", "our", "their", + "mine", "yours", "hers", "theirs", "myself", "yourself", "himself", + "herself", "itself", "ourselves", "yourselves", "themselves", "this", + "that", "these", "those", "who", "whom", "whose", "what", "which", + "some", "any", "none", "all", "many", "few", "several", + "everyone", "somebody", "anybody", "nobody", "everything", "anything", + "nothing", "each", "every", "either", "neither" + }; + + public static string[] Prepositions => new string[] + { + "about", "above", "across", "after", "against", "along", "amid", "among", + "around", "as", "at", "before", "behind", "below", "beneath", "beside", + "besides", "between", "beyond", "but", "by", "despite", "down", "during", + "except", "for", "from", "in", "inside", "into", "like", "near", "of", "off", + "on", "onto", "out", "outside", "over", "past", "since", "through", "throughout", + "till", "to", "toward", "under", "underneath", "until", "up", "upon", "with", + "within", "without" + }; + + public static string[] Conjunctions => new string[] + { + "and", "but", "or", "nor", "for", "yet", "so", "if", "because", "although", "though", + "since", "until", "unless", "while", "whereas", "when", "where", "before", "after" + }; + + public static string[] Interjections => new string[] + { + "o", "ah", "aha", "alas", "aw", "aye", "eh", "hmm", "huh", "hurrah", "no", "oh", "oops", + "ouch", "ow", "phew", "shh", "tsk", "ugh", "um", "wow", "yay", "yes", "yikes" + }; + } +} diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs new file mode 100644 index 00000000..01dbdbfd --- /dev/null +++ b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using TagCloud.WordFilters; + +namespace TagCloud.Tests.WordFiltersTests +{ + [TestFixture] + internal class WordFilterChangeBannedWordsTest + { + private WordFilter wordFilter; + + [SetUp] + public void SetUp() + { + wordFilter = new WordFilter(); + } + + [Test] + public void Clear_ShouldClearBannedWordList() + { + wordFilter.Clear(); + wordFilter.BannedWords.Should().BeEmpty(); + } + + [TestCase("WordToAdd")] + public void Add_ShouldAddWord_ToBannedWords(string word) + { + wordFilter.Clear(); + wordFilter.Add(word); + wordFilter.BannedWords.Should().Contain(word).And.HaveCount(1); + } + + [TestCase("WordToRemove")] + public void Remove_ShouldRemoveWord_InBannedWords(string word) + { + wordFilter.Clear(); + wordFilter.Add(word); + wordFilter.Remove(word); + wordFilter.BannedWords.Should().NotContain(word); + } + } +} diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs new file mode 100644 index 00000000..6d6f9a43 --- /dev/null +++ b/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs @@ -0,0 +1,59 @@ +using FluentAssertions; +using TagCloud.WordFilters; + +namespace TagCloud.Tests.WordFiltersTests +{ + [TestFixture] + internal class WordFilterDefaultBannedWordsTest + { + private readonly WordFilter wordFilter = new WordFilter(); + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.CustomBans))] + public void IsCorrectWord_ShouldBeFalse_WithCustomBans(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.ToHaveForms))] + public void IsCorrectWord_ShouldBeFalse_WithToHaveForms(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.ToBeForms))] + public void IsCorrectWord_ShouldBeFalse_WithToBeForms(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Articles))] + public void IsCorrectWord_ShouldBeFalse_WithArticles(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Pronouns))] + public void IsCorrectWord_ShouldBeFalse_WithPronouns(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Prepositions))] + public void IsCorrectWord_ShouldBeFalse_WithPrepositions(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Conjunctions))] + public void IsCorrectWord_ShouldBeFalse_WithConjunctions(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Interjections))] + public void IsCorrectWord_ShouldBeFalse_WithInterjections(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + } +} diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs index 1fa3f690..1f0aea18 100644 --- a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs +++ b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs @@ -1,5 +1,4 @@ -using Microsoft.VisualBasic; -using TagCloud.WordReaders; +using TagCloud.WordReaders; namespace TagCloud.Tests.WordReadersTests { @@ -36,8 +35,16 @@ internal class WordReaderTest public void Init() { Directory.CreateDirectory(directoryPath); - File.WriteAllLines(Path.Combine(directoryPath, fileWithCorrectValuesPath), correctValues); - File.WriteAllLines(Path.Combine(directoryPath, fileWithIncorrectValuesPath), incorrectValues); + File.WriteAllLines( + Path.Combine( + directoryPath, + fileWithCorrectValuesPath), + correctValues); + File.WriteAllLines + (Path.Combine( + directoryPath, + fileWithIncorrectValuesPath), + incorrectValues); } [SetUp] From b99e3acbf58f5d051842155d41fe3c6604b5929d Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 10:21:16 +0500 Subject: [PATCH 13/17] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=B4=D0=B0=20Fon?= =?UTF-8?q?tParser.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FontParser теперь возвращает FontFamily --- TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs | 4 ++-- TagCloud/Parsers/FontParser.cs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index 6c46b381..ca883812 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -6,11 +6,11 @@ internal class CloudLayouterPainter( Size imageSize, Color? backgroundColor = null, Color? textColor = null, - string? fontName = null) : ICloudLayouterPainter + FontFamily? fontName = null) : ICloudLayouterPainter { private readonly Color backgroundColor = backgroundColor ?? Color.White; private readonly Color textColor = textColor ?? Color.Black; - private readonly string fontName = fontName ?? "Arial"; + private readonly FontFamily fontName = fontName ?? new FontFamily("Arial"); public Bitmap Draw(IList tags) { diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index 46c97ada..4564662d 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -4,15 +4,14 @@ namespace TagCloud.Parsers { internal static class FontParser { - public static string ParseFont(string font) + public static FontFamily ParseFont(string font) { if (!FontFamily.Families.Any( x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException($"Неизвестный шрифт {font}"); } - return font; + return new FontFamily(font); } - } } From 6d7e951be62b8fcc2ebf0c1b61dc47d26ec81977 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 10:24:53 +0500 Subject: [PATCH 14/17] =?UTF-8?q?=D0=91=D0=B8=D0=B7=D0=BD=D0=B5=D1=81=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=B8=D0=B7=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=D0=B0=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B2=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=81=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Добавлена фабрика по созданию WordFilter. 2. Добавлена фабрика по созданиюCloudLayouterWorker. 3. Рефакторинг DIContainer. --- TagCloud/DIContainer.cs | 115 ++++++++++-------- .../Factories/CloudLayouterWorkerFactory.cs | 39 ++++++ TagCloud/Factories/WordFilterFactory.cs | 53 ++++++++ 3 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 TagCloud/Factories/CloudLayouterWorkerFactory.cs create mode 100644 TagCloud/Factories/WordFilterFactory.cs diff --git a/TagCloud/DIContainer.cs b/TagCloud/DIContainer.cs index 13dbbb6e..a4f41961 100644 --- a/TagCloud/DIContainer.cs +++ b/TagCloud/DIContainer.cs @@ -9,51 +9,60 @@ using TagCloud.WordFilters; using TagCloud.WordReaders; using TagCloud.Parsers; +using TagCloud.Factories; +using System.Drawing; namespace TagCloud { public static class DIContainer { - // 1. Разбить содержимое этого метода на отдельны части - // 2. Добавить проверку корректностей значений: - // - options.ImageSize; - // - options.MaxRectangleWidth; - // - options.MaxRectangleHeight; public static IContainer ConfigureContainer(CommandLineOptions options) { var builder = new ContainerBuilder(); - builder - .RegisterType() - .As() - .SingleInstance(); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterIWordFillterSevice(builder, options); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); - builder - .RegisterType() - .As() - .SingleInstance(); + var imageSize = SizeParser.ParseImageSize(options.ImageSize); + RegisterICloudLayouterPainterSevice(builder, options, imageSize); - builder - .RegisterType() - .As() - .SingleInstance(); + RegisterICloudLayouterWorkerSevice(builder, options); - builder - .RegisterType() - .As() - .SingleInstance(); + RegisterProgramExecutorService(builder, options, imageSize); + + return builder.Build(); + } + private static void RegisterSimpleSevice(ContainerBuilder builder) + where TImplementation : TService + where TService : notnull + { builder - .RegisterType() - .As() + .RegisterType() + .As() .SingleInstance(); + } + private static void RegisterSimpleSevice(ContainerBuilder builder) + where TImplementation : notnull + { builder - .RegisterType() - .As() + .RegisterType() + .AsSelf() .SingleInstance(); + } - var imageSize = SizeParser.ParseImageSize(options.ImageSize); + private static void RegisterICloudLayouterPainterSevice( + ContainerBuilder builder, + CommandLineOptions options, + Size imageSize) + { var backgroundColor = ColorParser.ParseColor(options.BackgroundColor); var textColor = ColorParser.ParseColor(options.TextColor); var font = FontParser.ParseFont(options.Font); @@ -64,42 +73,50 @@ public static IContainer ConfigureContainer(CommandLineOptions options) .WithParameter("textColor", textColor) .WithParameter("fontName", font) .SingleInstance(); + } - var isSorted = BoolParser.ParseIsSorted(options.IsSorted); - builder.Register((c, p) => + private static void RegisterICloudLayouterWorkerSevice( + ContainerBuilder builder, + CommandLineOptions options) + { + builder.Register(c => { - var wordReader = c.Resolve(); - var wordCounter = c.Resolve(); - var normalizer = c.Resolve(); - var wordFilter = c.Resolve(); - - foreach (var word in wordReader.ReadByLines(options.DataFileName)) - { - var wordInLowerCase = word.ToLower(); - if (!wordFilter.IsCorrectWord(wordInLowerCase)) - { - continue; - } - wordCounter.AddWord(wordInLowerCase); - } - - var normalizedValues = normalizer.Normalize(wordCounter.Values); - return new NormalizedFrequencyBasedCloudLayouterWorker( + var factory = c.Resolve(); + return factory.Create( + options.DataFileName, options.MaxRectangleWidth, options.MaxRectangleHeight, - normalizedValues, - isSorted); + BoolParser.ParseIsSorted(options.IsSorted)); }).As().SingleInstance(); + } + private static void RegisterIWordFillterSevice( + ContainerBuilder builder, + CommandLineOptions options) + { + builder.Register(c => + { + var factory = c.Resolve(); + return factory.Create( + options.WordsToIncludeFileName, + options.WordsToExcludeFileName, + c.Resolve()); + }).As().SingleInstance(); + } + + private static void RegisterProgramExecutorService( + ContainerBuilder builder, + CommandLineOptions options, + Size imageSize) + { builder.RegisterType() .WithParameter("size", imageSize) + .WithParameter("resultFormat", options.ResultFormat) .WithParameter("maxRectangleWidth", options.MaxRectangleWidth) .WithParameter("maxRectangleHeight", options.MaxRectangleHeight) .WithParameter("imageFileName", options.ImageFileName) .WithParameter("dataFileName", options.DataFileName) .SingleInstance(); - - return builder.Build(); } } } diff --git a/TagCloud/Factories/CloudLayouterWorkerFactory.cs b/TagCloud/Factories/CloudLayouterWorkerFactory.cs new file mode 100644 index 00000000..e1de1a15 --- /dev/null +++ b/TagCloud/Factories/CloudLayouterWorkerFactory.cs @@ -0,0 +1,39 @@ +using TagCloud.CloudLayouterWorkers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud.Factories +{ + internal class CloudLayouterWorkerFactory( + IWordReader wordReader, + IWordCounter wordCounter, + INormalizer normalizer, + IWordFilter wordFilter) + { + public ICloudLayouterWorker Create( + string dataFileName, + int maxRectangleWidth, + int maxRectangleHeight, + bool isSorted) + { + foreach (var word in wordReader.ReadByLines(dataFileName)) + { + var wordInLowerCase = word.ToLower(); + if (!wordFilter.IsCorrectWord(wordInLowerCase)) + { + continue; + } + wordCounter.AddWord(wordInLowerCase); + } + + var normalizedValues = normalizer.Normalize(wordCounter.Values); + return new NormalizedFrequencyBasedCloudLayouterWorker( + maxRectangleWidth, + maxRectangleHeight, + normalizedValues, + isSorted); + } + } +} diff --git a/TagCloud/Factories/WordFilterFactory.cs b/TagCloud/Factories/WordFilterFactory.cs new file mode 100644 index 00000000..5cd197e3 --- /dev/null +++ b/TagCloud/Factories/WordFilterFactory.cs @@ -0,0 +1,53 @@ +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud.Factories +{ + internal class WordFilterFactory + { + public IWordFilter Create( + string wordsToIncludeFileName, + string wordsToExcludeFileName, + IWordReader wordReader) + { + var result = new WordFilter(); + + if (IsFileNameCorrect(wordsToIncludeFileName)) + { + AddWords(wordReader, wordsToIncludeFileName, result); + } + + if (IsFileNameCorrect(wordsToExcludeFileName)) + { + RemoveWords(wordReader, wordsToExcludeFileName, result); + } + + return result; + } + + private bool IsFileNameCorrect(string fileName) + => !string.IsNullOrEmpty(fileName) && !string.IsNullOrWhiteSpace(fileName); + + private void AddWords( + IWordReader wordReader, + string wordsToIncludeFileName, + WordFilter wordFilter) + { + foreach (var word in wordReader.ReadByLines(wordsToIncludeFileName)) + { + wordFilter.Add(word.ToLower()); + } + } + + private void RemoveWords( + IWordReader wordReader, + string wordsToExcludeFileName, + WordFilter wordFilter) + { + foreach (var word in wordReader.ReadByLines(wordsToExcludeFileName)) + { + wordFilter.Remove(word.ToLower()); + } + } + } +} From 1a21dbe5eed24adc4877d14e0bea025badc6329b Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 10:27:59 +0500 Subject: [PATCH 15/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=B0?= =?UTF-8?q?=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=BD=D0=BE=D0=B9?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. wordsToIncludeFile - добавление слов в фильтр "скучных слов" из файла. 2. wordsToExcludeFile- исключение слов из фильтра "скучных слов" из файла. 3. Рефакторинг WordFilter. --- TagCloud/CommandLineOptions.cs | 17 ++++++++++++----- TagCloud/WordFilters/WordFilter.cs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/TagCloud/CommandLineOptions.cs b/TagCloud/CommandLineOptions.cs index 44706271..a67eddbc 100644 --- a/TagCloud/CommandLineOptions.cs +++ b/TagCloud/CommandLineOptions.cs @@ -2,10 +2,6 @@ namespace TagCloud { - // 1. Нужно добавить опцию для указания имени файла, - // который содержит слова, для иссключения из фильтра скучных слов - // 2. Нужно добавить опцию для указания имени файла, - // который содержит слова, для добавления в фильтр скучных слов public class CommandLineOptions { [Option( @@ -67,6 +63,17 @@ public class CommandLineOptions Required = false, HelpText = "Формат создаваемого изображение, например png.")] public string ResultFormat { get; set; } = "png"; - } + [Option( + "wordsToIncludeFile", + Required = false, + HelpText = "Имя файла со словами для добавления в фильтр \"скучных слов\".")] + public string WordsToIncludeFileName { get; set; } = string.Empty; + + [Option( + "wordsToExcludeFile", + Required = false, + HelpText = "Имя файла со словами для исключения из фильтра \"скучных слов\".")] + public string WordsToExcludeFileName { get; set; } = string.Empty; + } } \ No newline at end of file diff --git a/TagCloud/WordFilters/WordFilter.cs b/TagCloud/WordFilters/WordFilter.cs index add9c3eb..81900028 100644 --- a/TagCloud/WordFilters/WordFilter.cs +++ b/TagCloud/WordFilters/WordFilter.cs @@ -65,7 +65,7 @@ public WordFilter(IList? toAdd = null, IList? toExclude = null) public void Clear() => bannedWords.Clear(); - public HashSet BannedWords => bannedWords; + public HashSet BannedWords => bannedWords.ToHashSet(); public bool IsCorrectWord(string word) { From e8506d65dc7963fb5d759b6b254d9fd1a49241cb Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 10:28:34 +0500 Subject: [PATCH 16/17] =?UTF-8?q?=D0=9E=D0=B1=D1=89=D0=B8=D0=B9=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=B4=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud.Tests/MainTest.cs | 2 +- TagCloud/GlobalSuppressions.cs | 2 ++ TagCloud/Parsers/BoolParser.cs | 4 ++-- TagCloud/Program.cs | 9 +++------ TagCloud/ProgramExecutor.cs | 19 ++++++------------- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/TagCloud.Tests/MainTest.cs b/TagCloud.Tests/MainTest.cs index 76bf6026..8673add3 100644 --- a/TagCloud.Tests/MainTest.cs +++ b/TagCloud.Tests/MainTest.cs @@ -47,7 +47,7 @@ public void Program_ExecutesSuccessfully_WithValidArguments(string format) using var scope = container.BeginLifetimeScope(); var executor = scope.Resolve(); - Assert.DoesNotThrow(() => executor.Execute(format)); + Assert.DoesNotThrow(() => executor.Execute()); File.Exists($"{imageFile}.{format}").Should().BeTrue(); } diff --git a/TagCloud/GlobalSuppressions.cs b/TagCloud/GlobalSuppressions.cs index 75089be8..3069505e 100644 --- a/TagCloud/GlobalSuppressions.cs +++ b/TagCloud/GlobalSuppressions.cs @@ -13,3 +13,5 @@ [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:Program.ParseFont(System.String)~System.String")] [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ImageSavers.ImageSaver.SaveFile(System.Drawing.Bitmap,System.String,System.String)")] [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~System.Drawing.FontFamily")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~F:TagCloud.CloudLayouterPainters.CloudLayouterPainter.fontName")] diff --git a/TagCloud/Parsers/BoolParser.cs b/TagCloud/Parsers/BoolParser.cs index 82b78e87..419a6e24 100644 --- a/TagCloud/Parsers/BoolParser.cs +++ b/TagCloud/Parsers/BoolParser.cs @@ -4,9 +4,9 @@ internal static class BoolParser { public static bool ParseIsSorted(string value) { - if (value == false.ToString() || value == true.ToString()) + if (value == bool.FalseString || value == bool.TrueString) { - return value == true.ToString(); + return Convert.ToBoolean(value); } throw new ArgumentException($"Неизвестный параметр сортировки {value}"); } diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 47680d12..d67ab880 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -7,16 +7,13 @@ namespace TagCloud { internal class Program { - private static IContainer container; - static void Main(string[] args) { var parserResult = Parser.Default.ParseArguments(args); parserResult.WithParsed(options => { - container = DIContainer.ConfigureContainer(options); - Run(options.ResultFormat); + Run(DIContainer.ConfigureContainer(options)); }); parserResult.WithNotParsed(errors => @@ -27,11 +24,11 @@ static void Main(string[] args) }); } - private static void Run(string resultFormat) + private static void Run(IContainer container) { using var scope = container.BeginLifetimeScope(); var program = scope.Resolve(); - program.Execute(resultFormat); + program.Execute(); } } } diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs index c50e0d2e..24d4f246 100644 --- a/TagCloud/ProgramExecutor.cs +++ b/TagCloud/ProgramExecutor.cs @@ -1,34 +1,27 @@ -using System.Drawing; -using TagCloud.CloudLayouters; +using TagCloud.CloudLayouters; using TagCloud.CloudLayouterPainters; using TagCloud.CloudLayouterWorkers; using TagCloud.ImageSavers; -using TagCloud.Normalizers; -using TagCloud.WordCounters; -using TagCloud.WordFilters; -using TagCloud.WordReaders; namespace TagCloud { internal class ProgramExecutor( string imageFileName, - IWordCounter wordCounter, - INormalizer normalizer, + string resultFormat, ICloudLayouter layouter, ICloudLayouterPainter painter, ICloudLayouterWorker worker, IImageSaver imageSaver) { - public void Execute(string resultFormat) + public void Execute() { - var normalizedWordWeights = normalizer.Normalize(wordCounter.Values); - var tags = new List(); foreach (var rectangleProperty in worker.GetNextRectangleProperties()) { - tags.Add(new Tag(rectangleProperty.word, layouter.PutNextRectangle(rectangleProperty.size))); + var tagSize = layouter.PutNextRectangle(rectangleProperty.size); + var newTag = new Tag(rectangleProperty.word, tagSize); + tags.Add(newTag); } - imageSaver.SaveFile(painter.Draw(tags), imageFileName, resultFormat); } } From fe52cebb11435aba75d3d6d0618d3a6c6d884968 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 10:40:07 +0500 Subject: [PATCH 17/17] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BE=D0=BF=D1=86=D0=B8=D0=B9?= =?UTF-8?q?=20wordsToIncludeFile=20=D0=B8=20wordsToExcludeFile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud/CommandLineOptions.cs | 4 ++-- TagCloud/Factories/WordFilterFactory.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/TagCloud/CommandLineOptions.cs b/TagCloud/CommandLineOptions.cs index a67eddbc..457b37b3 100644 --- a/TagCloud/CommandLineOptions.cs +++ b/TagCloud/CommandLineOptions.cs @@ -68,12 +68,12 @@ public class CommandLineOptions "wordsToIncludeFile", Required = false, HelpText = "Имя файла со словами для добавления в фильтр \"скучных слов\".")] - public string WordsToIncludeFileName { get; set; } = string.Empty; + public string? WordsToIncludeFileName { get; set; } = null; [Option( "wordsToExcludeFile", Required = false, HelpText = "Имя файла со словами для исключения из фильтра \"скучных слов\".")] - public string WordsToExcludeFileName { get; set; } = string.Empty; + public string? WordsToExcludeFileName { get; set; } = null; } } \ No newline at end of file diff --git a/TagCloud/Factories/WordFilterFactory.cs b/TagCloud/Factories/WordFilterFactory.cs index 5cd197e3..51ec5852 100644 --- a/TagCloud/Factories/WordFilterFactory.cs +++ b/TagCloud/Factories/WordFilterFactory.cs @@ -6,26 +6,26 @@ namespace TagCloud.Factories internal class WordFilterFactory { public IWordFilter Create( - string wordsToIncludeFileName, - string wordsToExcludeFileName, + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, IWordReader wordReader) { var result = new WordFilter(); if (IsFileNameCorrect(wordsToIncludeFileName)) { - AddWords(wordReader, wordsToIncludeFileName, result); + AddWords(wordReader, wordsToIncludeFileName!, result); } if (IsFileNameCorrect(wordsToExcludeFileName)) { - RemoveWords(wordReader, wordsToExcludeFileName, result); + RemoveWords(wordReader, wordsToExcludeFileName!, result); } return result; } - private bool IsFileNameCorrect(string fileName) + private bool IsFileNameCorrect(string? fileName) => !string.IsNullOrEmpty(fileName) && !string.IsNullOrWhiteSpace(fileName); private void AddWords(