From 1825673d2a4abfb0a59aaaaefef4d1e7773049b7 Mon Sep 17 00:00:00 2001 From: HardreaM Date: Sat, 20 Jan 2024 20:00:50 +0500 Subject: [PATCH 1/6] Initial structure --- TagCloud/CloudDrawer/CloudDrawer.cs | 46 +++++++++ TagCloud/CloudSaver/CloudSaver.cs | 14 +++ TagCloud/CloudSaver/ICloudSaver.cs | 8 ++ TagCloud/Layouter/CircularCloudLayouter.cs | 38 ++++++++ TagCloud/Layouter/ICircularCloudLayouter.cs | 8 ++ TagCloud/PointGenerator/IPointGenerator.cs | 8 ++ TagCloud/PointGenerator/SpiralGenerator.cs | 30 ++++++ TagCloud/Program.cs | 26 +++++ TagCloud/TagCloud.csproj | 14 +++ TagCloudTests/CircularCloudLayouter_Should.cs | 96 +++++++++++++++++++ TagCloudTests/SpiralGenerator_Should.cs | 44 +++++++++ TagCloudTests/TagCloudTests.csproj | 21 ++++ TagCloudTests/Usings.cs | 4 + di.sln | 12 +++ 14 files changed, 369 insertions(+) create mode 100644 TagCloud/CloudDrawer/CloudDrawer.cs create mode 100644 TagCloud/CloudSaver/CloudSaver.cs create mode 100644 TagCloud/CloudSaver/ICloudSaver.cs create mode 100644 TagCloud/Layouter/CircularCloudLayouter.cs create mode 100644 TagCloud/Layouter/ICircularCloudLayouter.cs create mode 100644 TagCloud/PointGenerator/IPointGenerator.cs create mode 100644 TagCloud/PointGenerator/SpiralGenerator.cs create mode 100644 TagCloud/Program.cs create mode 100644 TagCloud/TagCloud.csproj create mode 100644 TagCloudTests/CircularCloudLayouter_Should.cs create mode 100644 TagCloudTests/SpiralGenerator_Should.cs create mode 100644 TagCloudTests/TagCloudTests.csproj create mode 100644 TagCloudTests/Usings.cs diff --git a/TagCloud/CloudDrawer/CloudDrawer.cs b/TagCloud/CloudDrawer/CloudDrawer.cs new file mode 100644 index 000000000..eb1956628 --- /dev/null +++ b/TagCloud/CloudDrawer/CloudDrawer.cs @@ -0,0 +1,46 @@ +using System.Drawing; + +namespace TagCloud; + +public static class CloudDrawer +{ + public static Bitmap DrawTagCloud(IList rectangles, int border = 10) + { + var imageSize = GetImageSize(rectangles, border); + var shift = GetImageShift(rectangles, border); + var image = new Bitmap(imageSize.Width, imageSize.Height); + using var graphics = Graphics.FromImage(image); + foreach (var rectangle in rectangles) + { + var shiftedCoordinates = new Point(rectangle.X - shift.Width, rectangle.Y - shift.Height); + using var brush = new SolidBrush(GetRandomColor()); + graphics.FillRectangle(brush, new Rectangle(shiftedCoordinates, rectangle.Size)); + } + + return image; + } + + private static Size GetImageShift(IList rectangles, int border) + { + var minX = rectangles.Min(rectangle => rectangle.Left); + var minY = rectangles.Min(rectangle => rectangle.Top); + + return new Size(minX - border, minY - border); + } + + private static Size GetImageSize(IList rectangles, int border) + { + var minX = rectangles.Min(rectangle => rectangle.Left); + var maxX = rectangles.Max(rectangle => rectangle.Right); + var minY = rectangles.Min(rectangle => rectangle.Top); + var maxY = rectangles.Max(rectangle => rectangle.Bottom); + + return new Size(maxX - minX + 2 * border, maxY - minY + 2 * border); + } + + private static Color GetRandomColor() + { + var random = new Random(); + return Color.FromArgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)); + } +} \ No newline at end of file diff --git a/TagCloud/CloudSaver/CloudSaver.cs b/TagCloud/CloudSaver/CloudSaver.cs new file mode 100644 index 000000000..3a28b34f3 --- /dev/null +++ b/TagCloud/CloudSaver/CloudSaver.cs @@ -0,0 +1,14 @@ +using System.Drawing; + +namespace TagCloud.CloudSaver; + +public class CloudSaver : ICloudSaver +{ + public void Save(Bitmap bitmap, string outputPath, string imageFormat) + { + using (bitmap) + { + bitmap.Save($"{outputPath}.{imageFormat}"); + } + } +} \ No newline at end of file diff --git a/TagCloud/CloudSaver/ICloudSaver.cs b/TagCloud/CloudSaver/ICloudSaver.cs new file mode 100644 index 000000000..16342e39e --- /dev/null +++ b/TagCloud/CloudSaver/ICloudSaver.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagCloud.CloudSaver; + +public interface ICloudSaver +{ + void Save(Bitmap bitmap, string OutputPath, string imageFormat); +} \ No newline at end of file diff --git a/TagCloud/Layouter/CircularCloudLayouter.cs b/TagCloud/Layouter/CircularCloudLayouter.cs new file mode 100644 index 000000000..3d593658e --- /dev/null +++ b/TagCloud/Layouter/CircularCloudLayouter.cs @@ -0,0 +1,38 @@ +using System.Drawing; + +namespace TagCloud; + +public class CircularCloudLayouter : ICircularCloudLayouter +{ + private readonly IPointGenerator pointGenerator; + public IList Rectangles { get; } + + public CircularCloudLayouter(IPointGenerator pointGenerator) + { + Rectangles = new List(); + this.pointGenerator = pointGenerator; + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) + { + throw new ArgumentException("Rectangle size parameters should be positive"); + } + + var rectangle = new Rectangle(pointGenerator.GetNextPoint(), rectangleSize); + while (!CanPlaceRectangle(rectangle)) + { + rectangle = new Rectangle(pointGenerator.GetNextPoint() - rectangleSize / 2, rectangleSize); + } + + Rectangles.Add(rectangle); + + return rectangle; + } + + private bool CanPlaceRectangle(Rectangle newRectangle) + { + return !Rectangles.Any(rectangle => rectangle.IntersectsWith(newRectangle)); + } +} \ No newline at end of file diff --git a/TagCloud/Layouter/ICircularCloudLayouter.cs b/TagCloud/Layouter/ICircularCloudLayouter.cs new file mode 100644 index 000000000..dee68e17f --- /dev/null +++ b/TagCloud/Layouter/ICircularCloudLayouter.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagCloud; + +public interface ICircularCloudLayouter +{ + Rectangle PutNextRectangle(Size rectangleSize); +} \ No newline at end of file diff --git a/TagCloud/PointGenerator/IPointGenerator.cs b/TagCloud/PointGenerator/IPointGenerator.cs new file mode 100644 index 000000000..b584fd208 --- /dev/null +++ b/TagCloud/PointGenerator/IPointGenerator.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagCloud; + +public interface IPointGenerator +{ + Point GetNextPoint(); +} \ No newline at end of file diff --git a/TagCloud/PointGenerator/SpiralGenerator.cs b/TagCloud/PointGenerator/SpiralGenerator.cs new file mode 100644 index 000000000..74302738d --- /dev/null +++ b/TagCloud/PointGenerator/SpiralGenerator.cs @@ -0,0 +1,30 @@ +using System.Drawing; + +namespace TagCloud; + +public class SpiralGenerator : IPointGenerator +{ + private readonly Point startPoint; + private readonly int spiralDensity; + private readonly double angleShift; + private double currentAngle; + + public SpiralGenerator(Point startPoint, int spiralDensity = 1, double angleShift = 0.1) + { + if (startPoint.X < 0 || startPoint.Y < 0) + throw new ArgumentException("Spiral center point coordinates should be non-negative"); + this.startPoint = startPoint; + this.spiralDensity = spiralDensity; + this.angleShift = angleShift; + } + + public Point GetNextPoint() + { + var radius = spiralDensity * currentAngle; + var x = (int)(Math.Cos(currentAngle) * radius); + var y = (int)(Math.Sin(currentAngle) * radius); + currentAngle += angleShift; + + return new Point(startPoint.X + x, startPoint.Y + y); + } +} \ No newline at end of file diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs new file mode 100644 index 000000000..7c6e27a24 --- /dev/null +++ b/TagCloud/Program.cs @@ -0,0 +1,26 @@ +using System.Drawing; + +namespace TagCloud; + + public class Program + { + private const int Width = 1920; + private const int Height = 1080; + + static void Main(string[] args) + { + var layouter = new CircularCloudLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2), 1, 0.01)); + + var random = new Random(); + + for (var i = 0; i < 150; i++) + { + layouter.PutNextRectangle(new Size(50 + random.Next(0, 100), 50 + random.Next(0, 100))); + } + + var saver = new CloudSaver.CloudSaver(); + + using var bitmap = CloudDrawer.DrawTagCloud(layouter.Rectangles); + saver.Save(bitmap, "Sample", "png"); + } + } \ No newline at end of file diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj new file mode 100644 index 000000000..ce0c742c6 --- /dev/null +++ b/TagCloud/TagCloud.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0-windows + enable + enable + + + + + + + diff --git a/TagCloudTests/CircularCloudLayouter_Should.cs b/TagCloudTests/CircularCloudLayouter_Should.cs new file mode 100644 index 000000000..5c802912f --- /dev/null +++ b/TagCloudTests/CircularCloudLayouter_Should.cs @@ -0,0 +1,96 @@ +using NUnit.Framework.Interfaces; + +[TestFixture] +public class CircularCloudLayouter_Should +{ + private const int Width = 1920; + private const int Height = 1080; + private CircularCloudLayouter sut; + + + [SetUp] + public void Setup() + { + sut = new CircularCloudLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); + } + + [TearDown] + public void TearDown() + { + if (TestContext.CurrentContext.Result.Outcome == ResultState.Failure) + { + var bitmap = CloudDrawer.DrawTagCloud(sut.Rectangles); + + var path = @$"{Environment.CurrentDirectory}\..\..\..\FailedTests\{this.GetType()}"; + var absPath = Path.GetFullPath(path); + + if (!Directory.Exists(absPath)) + { + Directory.CreateDirectory(absPath); + } + + var fileName = TestContext.CurrentContext.Test.Name; + absPath += @$"\{fileName}.png"; + + bitmap.Save(absPath); + + TestContext.Out.WriteLine($"Tag cloud visualization saved to file <{absPath}>"); + } + } + + [Test] + public void HaveEmptyRectanglesList_WhenCreated() + { + sut.Rectangles.Count.Should().Be(0); + } + + [TestCase(1, TestName = "One rectangle")] + [TestCase(10, TestName = "10 rectangles")] + public void HaveAllRectangles_WhichWerePut(int rectanglesCount) + { + for (var i = 0; i < rectanglesCount; i++) + { + sut.PutNextRectangle(new Size(2, 3)); + } + + sut.Rectangles.Count.Should().Be(rectanglesCount); + } + + [TestCase(0, 5, TestName = "Width is zero")] + [TestCase(5, 0, TestName = "Height is zero")] + [TestCase(-5, 5, TestName = "Width is negative")] + [TestCase(5, -5, TestName = "Height is negative")] + public void ThrowException_OnInvalidSizeArguments(int width, int height) + { + Action action = () => { sut.PutNextRectangle(new Size(0, 0)); }; + + action.Should().Throw(); + } + + [Test] + public void ContainNonIntersectingRectangles() + { + var random = new Random(); + + for (var i = 0; i < 50; i++) + { + sut.PutNextRectangle(new Size(50 + random.Next(0, 100), 50 + random.Next(0, 100))); + } + + HasIntersectedRectangles(sut.Rectangles).Should().Be(false); + } + + private bool HasIntersectedRectangles(IList rectangles) + { + for (var i = 0; i < rectangles.Count - 1; i++) + { + for (var j = i + 1; j < rectangles.Count; j++) + { + if (rectangles[i].IntersectsWith(rectangles[j])) + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/TagCloudTests/SpiralGenerator_Should.cs b/TagCloudTests/SpiralGenerator_Should.cs new file mode 100644 index 000000000..165e81e04 --- /dev/null +++ b/TagCloudTests/SpiralGenerator_Should.cs @@ -0,0 +1,44 @@ +[TestFixture] +public class SpiralGenerator_Should +{ + private const int Width = 1920; + private const int Height = 1080; + private SpiralGenerator sut; + private Point startPoint; + + [SetUp] + public void Setup() + { + startPoint = new Point(Width / 2, Height / 2); + sut = new SpiralGenerator(startPoint); + } + + [Test] + public void ReturnCenterPoint_OnFirstCall() + { + sut.GetNextPoint().Should().BeEquivalentTo(startPoint); + } + + [Test] + public void ReturnDifferentPoints_AfterMultipleCalls() + { + var spiralPoints = new HashSet(); + + for (var i = 0; i < 50; i++) + { + spiralPoints.Add(sut.GetNextPoint()); + } + + spiralPoints.Count.Should().BeGreaterThan(1); + } + + [TestCase(-1, 1, TestName = "X is negative")] + [TestCase(1, -1, TestName = "Y is negative")] + [TestCase(-1, -1, TestName = "X and Y are negative")] + public void ThrowException_OnInvalidCenterPoint(int x, int y) + { + Action action = () => { new SpiralGenerator(new Point(x, y)); }; + + action.Should().Throw(); + } +} \ No newline at end of file diff --git a/TagCloudTests/TagCloudTests.csproj b/TagCloudTests/TagCloudTests.csproj new file mode 100644 index 000000000..dc210dce6 --- /dev/null +++ b/TagCloudTests/TagCloudTests.csproj @@ -0,0 +1,21 @@ + + + + net8.0-windows + enable + enable + + false + + + + + + + + + + + + + diff --git a/TagCloudTests/Usings.cs b/TagCloudTests/Usings.cs new file mode 100644 index 000000000..663d37242 --- /dev/null +++ b/TagCloudTests/Usings.cs @@ -0,0 +1,4 @@ +global using NUnit.Framework; +global using FluentAssertions; +global using TagCloud; +global using System.Drawing; \ No newline at end of file diff --git a/di.sln b/di.sln index b27b7c05d..887024570 100644 --- a/di.sln +++ b/di.sln @@ -2,6 +2,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FractalPainter", "FractalPainter\FractalPainter.csproj", "{4D70883B-6F8B-4166-802F-8EDC9BE93199}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloud", "TagCloud\TagCloud.csproj", "{08924E6D-5ECE-4367-A043-75EE79AE421C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloudTests", "TagCloudTests\TagCloudTests.csproj", "{2B0195B5-2D91-439A-8B3C-913D41F05275}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +16,13 @@ Global {4D70883B-6F8B-4166-802F-8EDC9BE93199}.Debug|Any CPU.Build.0 = Debug|Any CPU {4D70883B-6F8B-4166-802F-8EDC9BE93199}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D70883B-6F8B-4166-802F-8EDC9BE93199}.Release|Any CPU.Build.0 = Release|Any CPU + {08924E6D-5ECE-4367-A043-75EE79AE421C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08924E6D-5ECE-4367-A043-75EE79AE421C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08924E6D-5ECE-4367-A043-75EE79AE421C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08924E6D-5ECE-4367-A043-75EE79AE421C}.Release|Any CPU.Build.0 = Release|Any CPU + {2B0195B5-2D91-439A-8B3C-913D41F05275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B0195B5-2D91-439A-8B3C-913D41F05275}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B0195B5-2D91-439A-8B3C-913D41F05275}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B0195B5-2D91-439A-8B3C-913D41F05275}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 37658f9e42f6dd5c351094ffa3007a68d742dfa0 Mon Sep 17 00:00:00 2001 From: HardreaM Date: Sun, 21 Jan 2024 22:25:40 +0500 Subject: [PATCH 2/6] New project structure --- TagCloud/CloudDrawer/CloudDrawer.cs | 2 +- TagCloud/FileReader/FileReader.cs | 12 +++++++++ TagCloud/FileReader/IFileReader.cs | 6 +++++ TagCloud/Layouter/CircularCloudLayouter.cs | 3 ++- TagCloud/Layouter/ICircularCloudLayouter.cs | 2 +- TagCloud/PointGenerator/IPointGenerator.cs | 2 +- TagCloud/PointGenerator/SpiralGenerator.cs | 2 +- TagCloud/Program.cs | 4 ++- TagCloud/Settings/IAppSettings.cs | 12 +++++++++ TagCloud/Settings/Settings.cs | 27 +++++++++++++++++++ TagCloud/TagCloud.csproj | 1 + TagCloud/UserInterface/ConsoleUI.cs | 16 +++++++++++ TagCloud/UserInterface/IUserInterface.cs | 8 ++++++ TagCloud/WordHandler/IWordHandler.cs | 6 +++++ TagCloud/WordHandler/WordRankerByFrequency.cs | 9 +++++++ .../WordPreprocessor/DefaultPreprocessor.cs | 9 +++++++ .../WordPreprocessor/IWordPreprocessor.cs | 6 +++++ TagCloudTests/CircularCloudLayouter_Should.cs | 3 +++ TagCloudTests/SpiralGenerator_Should.cs | 4 ++- 19 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 TagCloud/FileReader/FileReader.cs create mode 100644 TagCloud/FileReader/IFileReader.cs create mode 100644 TagCloud/Settings/IAppSettings.cs create mode 100644 TagCloud/Settings/Settings.cs create mode 100644 TagCloud/UserInterface/ConsoleUI.cs create mode 100644 TagCloud/UserInterface/IUserInterface.cs create mode 100644 TagCloud/WordHandler/IWordHandler.cs create mode 100644 TagCloud/WordHandler/WordRankerByFrequency.cs create mode 100644 TagCloud/WordPreprocessor/DefaultPreprocessor.cs create mode 100644 TagCloud/WordPreprocessor/IWordPreprocessor.cs diff --git a/TagCloud/CloudDrawer/CloudDrawer.cs b/TagCloud/CloudDrawer/CloudDrawer.cs index eb1956628..9dd15f054 100644 --- a/TagCloud/CloudDrawer/CloudDrawer.cs +++ b/TagCloud/CloudDrawer/CloudDrawer.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloud; +namespace TagCloud.CloudDrawer; public static class CloudDrawer { diff --git a/TagCloud/FileReader/FileReader.cs b/TagCloud/FileReader/FileReader.cs new file mode 100644 index 000000000..bc8fc6c26 --- /dev/null +++ b/TagCloud/FileReader/FileReader.cs @@ -0,0 +1,12 @@ +namespace TagCloud.FileReader; + +public class FileReader : IFileReader +{ + public IEnumerable ReadLines(string InputPath) + { + if (!File.Exists(InputPath)) + throw new ArgumentException("Source file doesn't exist"); + + return File.ReadLines(InputPath); + } +} \ No newline at end of file diff --git a/TagCloud/FileReader/IFileReader.cs b/TagCloud/FileReader/IFileReader.cs new file mode 100644 index 000000000..9d87e706c --- /dev/null +++ b/TagCloud/FileReader/IFileReader.cs @@ -0,0 +1,6 @@ +namespace TagCloud.FileReader; + +public interface IFileReader +{ + +} \ No newline at end of file diff --git a/TagCloud/Layouter/CircularCloudLayouter.cs b/TagCloud/Layouter/CircularCloudLayouter.cs index 3d593658e..e74d91ed5 100644 --- a/TagCloud/Layouter/CircularCloudLayouter.cs +++ b/TagCloud/Layouter/CircularCloudLayouter.cs @@ -1,6 +1,7 @@ using System.Drawing; +using TagCloud.PointGenerator; -namespace TagCloud; +namespace TagCloud.Layouter; public class CircularCloudLayouter : ICircularCloudLayouter { diff --git a/TagCloud/Layouter/ICircularCloudLayouter.cs b/TagCloud/Layouter/ICircularCloudLayouter.cs index dee68e17f..8df434315 100644 --- a/TagCloud/Layouter/ICircularCloudLayouter.cs +++ b/TagCloud/Layouter/ICircularCloudLayouter.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloud; +namespace TagCloud.Layouter; public interface ICircularCloudLayouter { diff --git a/TagCloud/PointGenerator/IPointGenerator.cs b/TagCloud/PointGenerator/IPointGenerator.cs index b584fd208..8388b83d7 100644 --- a/TagCloud/PointGenerator/IPointGenerator.cs +++ b/TagCloud/PointGenerator/IPointGenerator.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloud; +namespace TagCloud.PointGenerator; public interface IPointGenerator { diff --git a/TagCloud/PointGenerator/SpiralGenerator.cs b/TagCloud/PointGenerator/SpiralGenerator.cs index 74302738d..817d2ca43 100644 --- a/TagCloud/PointGenerator/SpiralGenerator.cs +++ b/TagCloud/PointGenerator/SpiralGenerator.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloud; +namespace TagCloud.PointGenerator; public class SpiralGenerator : IPointGenerator { diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 7c6e27a24..1f6801e35 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -1,4 +1,6 @@ using System.Drawing; +using TagCloud.Layouter; +using TagCloud.PointGenerator; namespace TagCloud; @@ -20,7 +22,7 @@ static void Main(string[] args) var saver = new CloudSaver.CloudSaver(); - using var bitmap = CloudDrawer.DrawTagCloud(layouter.Rectangles); + using var bitmap = CloudDrawer.CloudDrawer.DrawTagCloud(layouter.Rectangles); saver.Save(bitmap, "Sample", "png"); } } \ No newline at end of file diff --git a/TagCloud/Settings/IAppSettings.cs b/TagCloud/Settings/IAppSettings.cs new file mode 100644 index 000000000..b83be67ea --- /dev/null +++ b/TagCloud/Settings/IAppSettings.cs @@ -0,0 +1,12 @@ +namespace TagCloud.Settings; + +public interface IAppSettings +{ + public string InputPath { get; } + public string OutputPath { get; } + public string ImageExtension { get; } + public string FontType { get; } + public int CloudWidth { get; } + public int CloudHeight { get; } + public string LayouterType { get; } +} \ No newline at end of file diff --git a/TagCloud/Settings/Settings.cs b/TagCloud/Settings/Settings.cs new file mode 100644 index 000000000..8b67f0996 --- /dev/null +++ b/TagCloud/Settings/Settings.cs @@ -0,0 +1,27 @@ +using CommandLine; + +namespace TagCloud.Settings; + +public class Settings : IAppSettings +{ + [Option('s', "sourceFile", Required = true, HelpText = "Path to file with words to visualize")] + public string InputPath { get; set; } + + [Option('o', "outputPath", Default = "result", HelpText = "Path to output image file")] + public string OutputPath { get; set; } + + [Option('e', "outputFormat", Default = "png", HelpText = "Output image file format")] + public string ImageExtension { get; set; } + + [Option('f', "fontType", Default = "Roboto", HelpText = "Font type of words")] + public string FontType { get; set; } + + [Option('W', "width", Default = 1920, HelpText = "Width of cloud")] + public int CloudWidth { get; set; } + + [Option('H', "height", Default = 1080, HelpText = "Height of cloud")] + public int CloudHeight { get; set; } + + [Option('l', "layouter", Default = "Circular", HelpText = "Cloud layouter algorithm")] + public string LayouterType { get; set; } +} \ No newline at end of file diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj index ce0c742c6..79a73a0c4 100644 --- a/TagCloud/TagCloud.csproj +++ b/TagCloud/TagCloud.csproj @@ -8,6 +8,7 @@ + diff --git a/TagCloud/UserInterface/ConsoleUI.cs b/TagCloud/UserInterface/ConsoleUI.cs new file mode 100644 index 000000000..a6137a160 --- /dev/null +++ b/TagCloud/UserInterface/ConsoleUI.cs @@ -0,0 +1,16 @@ +using TagCloud.Settings; + +namespace TagCloud.UserInterface; + +public class ConsoleUI : IUserInterface +{ + public ConsoleUI() + { + + } + + public void Run(IAppSettings appSettings) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/TagCloud/UserInterface/IUserInterface.cs b/TagCloud/UserInterface/IUserInterface.cs new file mode 100644 index 000000000..a7cf4314c --- /dev/null +++ b/TagCloud/UserInterface/IUserInterface.cs @@ -0,0 +1,8 @@ +using TagCloud.Settings; + +namespace TagCloud.UserInterface; + +public interface IUserInterface +{ + void Run(IAppSettings appSettings); +} \ No newline at end of file diff --git a/TagCloud/WordHandler/IWordHandler.cs b/TagCloud/WordHandler/IWordHandler.cs new file mode 100644 index 000000000..30798e1f8 --- /dev/null +++ b/TagCloud/WordHandler/IWordHandler.cs @@ -0,0 +1,6 @@ +namespace TagCloud.WordHandler; + +public interface IWordHandler +{ + IEnumerable<(string word, int rank)> RankWords(IEnumerable words); +} \ No newline at end of file diff --git a/TagCloud/WordHandler/WordRankerByFrequency.cs b/TagCloud/WordHandler/WordRankerByFrequency.cs new file mode 100644 index 000000000..d687b32ee --- /dev/null +++ b/TagCloud/WordHandler/WordRankerByFrequency.cs @@ -0,0 +1,9 @@ +namespace TagCloud.WordHandler; + +public class WordRankerByFrequency : IWordHandler +{ + public IEnumerable<(string word, int rank)> RankWords(IEnumerable words) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/TagCloud/WordPreprocessor/DefaultPreprocessor.cs b/TagCloud/WordPreprocessor/DefaultPreprocessor.cs new file mode 100644 index 000000000..36c5113cd --- /dev/null +++ b/TagCloud/WordPreprocessor/DefaultPreprocessor.cs @@ -0,0 +1,9 @@ +namespace TagCloud.WordPreprocessor; + +public class DefaultPreprocessor : IWordPreprocessor +{ + public IEnumerable HandleWords(IEnumerable words) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/TagCloud/WordPreprocessor/IWordPreprocessor.cs b/TagCloud/WordPreprocessor/IWordPreprocessor.cs new file mode 100644 index 000000000..f562dcbe3 --- /dev/null +++ b/TagCloud/WordPreprocessor/IWordPreprocessor.cs @@ -0,0 +1,6 @@ +namespace TagCloud.WordPreprocessor; + +public interface IWordPreprocessor +{ + IEnumerable HandleWords(IEnumerable words); +} \ No newline at end of file diff --git a/TagCloudTests/CircularCloudLayouter_Should.cs b/TagCloudTests/CircularCloudLayouter_Should.cs index 5c802912f..939c1fac4 100644 --- a/TagCloudTests/CircularCloudLayouter_Should.cs +++ b/TagCloudTests/CircularCloudLayouter_Should.cs @@ -1,4 +1,7 @@ using NUnit.Framework.Interfaces; +using TagCloud.CloudDrawer; +using TagCloud.Layouter; +using TagCloud.PointGenerator; [TestFixture] public class CircularCloudLayouter_Should diff --git a/TagCloudTests/SpiralGenerator_Should.cs b/TagCloudTests/SpiralGenerator_Should.cs index 165e81e04..dc98eae2a 100644 --- a/TagCloudTests/SpiralGenerator_Should.cs +++ b/TagCloudTests/SpiralGenerator_Should.cs @@ -1,4 +1,6 @@ -[TestFixture] +using TagCloud.PointGenerator; + +[TestFixture] public class SpiralGenerator_Should { private const int Width = 1920; From 4690f1bafe30951fb4acd035f21990236d71f58a Mon Sep 17 00:00:00 2001 From: HardreaM Date: Tue, 23 Jan 2024 18:46:51 +0500 Subject: [PATCH 3/6] Complete base requirements --- TagCloud/CloudDrawer/CloudDrawer.cs | 71 +++- TagCloud/CloudDrawer/CustomPalette.cs | 18 + TagCloud/CloudDrawer/IDrawer.cs | 9 + TagCloud/CloudDrawer/IPalette.cs | 9 + TagCloud/CloudDrawer/RandomPalette.cs | 11 + TagCloud/CloudDrawer/Tag.cs | 17 + TagCloud/FileReader/IFileReader.cs | 2 +- ...ICircularCloudLayouter.cs => ILayouter.cs} | 3 +- .../{CircularCloudLayouter.cs => Layouter.cs} | 4 +- TagCloud/PointGenerator/SpiralGenerator.cs | 2 +- TagCloud/Program.cs | 49 ++- TagCloud/Settings/IAppSettings.cs | 1 + TagCloud/Settings/Settings.cs | 7 +- TagCloud/TagCloud.csproj | 1 + TagCloud/UserInterface/ConsoleUI.cs | 31 +- TagCloud/WordFilter/IFilter.cs | 8 + TagCloud/WordFilter/WordFilter.cs | 18 + TagCloud/WordHandler/WordRankerByFrequency.cs | 9 - .../WordPreprocessor/DefaultPreprocessor.cs | 9 - .../WordPreprocessor/IWordPreprocessor.cs | 6 - .../IWordRanker.cs} | 4 +- TagCloud/WordRanker/WordRankerByFrequency.cs | 10 + .../WordsPreprocessor/DefaultPreprocessor.cs | 9 + TagCloud/WordsPreprocessor/IPreprocessor.cs | 6 + TagCloud/result.png | Bin 0 -> 37179 bytes TagCloud/text.txt | 320 ++++++++++++++++++ TagCloudTests/ConsoleUI_Should.cs | 65 ++++ TagCloudTests/IOTests/CloudSaver_Should.cs | 25 ++ TagCloudTests/IOTests/FileReader_Should.cs | 33 ++ .../CircularCloudLayouter_Should.cs | 28 +- .../SpiralGenerator_Should.cs | 2 +- .../DefaultPreprocessor_Should.cs | 33 ++ .../WordProcessorsTests/WordFilter_Should.cs | 46 +++ .../WordProcessorsTests/WordRanker_Should.cs | 34 ++ 34 files changed, 804 insertions(+), 96 deletions(-) create mode 100644 TagCloud/CloudDrawer/CustomPalette.cs create mode 100644 TagCloud/CloudDrawer/IDrawer.cs create mode 100644 TagCloud/CloudDrawer/IPalette.cs create mode 100644 TagCloud/CloudDrawer/RandomPalette.cs create mode 100644 TagCloud/CloudDrawer/Tag.cs rename TagCloud/Layouter/{ICircularCloudLayouter.cs => ILayouter.cs} (62%) rename TagCloud/Layouter/{CircularCloudLayouter.cs => Layouter.cs} (89%) create mode 100644 TagCloud/WordFilter/IFilter.cs create mode 100644 TagCloud/WordFilter/WordFilter.cs delete mode 100644 TagCloud/WordHandler/WordRankerByFrequency.cs delete mode 100644 TagCloud/WordPreprocessor/DefaultPreprocessor.cs delete mode 100644 TagCloud/WordPreprocessor/IWordPreprocessor.cs rename TagCloud/{WordHandler/IWordHandler.cs => WordRanker/IWordRanker.cs} (57%) create mode 100644 TagCloud/WordRanker/WordRankerByFrequency.cs create mode 100644 TagCloud/WordsPreprocessor/DefaultPreprocessor.cs create mode 100644 TagCloud/WordsPreprocessor/IPreprocessor.cs create mode 100644 TagCloud/result.png create mode 100644 TagCloud/text.txt create mode 100644 TagCloudTests/ConsoleUI_Should.cs create mode 100644 TagCloudTests/IOTests/CloudSaver_Should.cs create mode 100644 TagCloudTests/IOTests/FileReader_Should.cs rename TagCloudTests/{ => LayouterTests}/CircularCloudLayouter_Should.cs (68%) rename TagCloudTests/{ => LayouterTests}/SpiralGenerator_Should.cs (95%) create mode 100644 TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs create mode 100644 TagCloudTests/WordProcessorsTests/WordFilter_Should.cs create mode 100644 TagCloudTests/WordProcessorsTests/WordRanker_Should.cs diff --git a/TagCloud/CloudDrawer/CloudDrawer.cs b/TagCloud/CloudDrawer/CloudDrawer.cs index 9dd15f054..4ef86a369 100644 --- a/TagCloud/CloudDrawer/CloudDrawer.cs +++ b/TagCloud/CloudDrawer/CloudDrawer.cs @@ -1,25 +1,74 @@ using System.Drawing; +using TagCloud.Layouter; +using TagCloud.Settings; namespace TagCloud.CloudDrawer; -public static class CloudDrawer +public class CloudDrawer : IDrawer { - public static Bitmap DrawTagCloud(IList rectangles, int border = 10) + private readonly ILayouter layouter; + private readonly IPalette palette; + private readonly IAppSettings appSettings; + private int minimalRank; + private int maximalRank; + private const int MaximalFontSize = 50; + private const int LengthSizeMultiplier = 35; + private const int ImageBorder = 10; + + public CloudDrawer(ILayouter layouter, IPalette palette, IAppSettings appSettings) + { + this.layouter = layouter; + this.palette = palette; + this.appSettings = appSettings; + } + + public Bitmap DrawTagCloud(IEnumerable<(string word, int rank)> words) { - var imageSize = GetImageSize(rectangles, border); - var shift = GetImageShift(rectangles, border); + var tags = PlaceWords(words); + var imageSize = GetImageSize(layouter.Rectangles, ImageBorder); + var shift = GetImageShift(layouter.Rectangles, ImageBorder); var image = new Bitmap(imageSize.Width, imageSize.Height); using var graphics = Graphics.FromImage(image); - foreach (var rectangle in rectangles) + using var background = new SolidBrush(palette.BackgroudColor); + graphics.FillRectangle(background, 0, 0, imageSize.Width, imageSize.Height); + foreach (var tag in tags) { - var shiftedCoordinates = new Point(rectangle.X - shift.Width, rectangle.Y - shift.Height); - using var brush = new SolidBrush(GetRandomColor()); - graphics.FillRectangle(brush, new Rectangle(shiftedCoordinates, rectangle.Size)); + var shiftedCoordinates = new PointF(tag.Position.X - shift.Width, tag.Position.Y - shift.Height); + using var brush = new SolidBrush(palette.ForegroundColor); + graphics.DrawString(tag.Value, new Font(FontFamily.GenericSerif, tag.FontSize), brush, shiftedCoordinates); } return image; } + private IList PlaceWords(IEnumerable<(string word, int rank)> words) + { + maximalRank = words.First().rank; + minimalRank = words.Last().rank - 1; + + var tags = new List(); + + foreach (var pair in words) + { + var fontSize = CalculateFontSize(pair.rank); + var boxLength = CalculateWordBoxLength(pair.word.Length, fontSize); + var rectangle = layouter.PutNextRectangle(new Size(boxLength, fontSize)); + tags.Add(new Tag(pair.word, rectangle, fontSize)); + } + + return tags; + } + + private int CalculateFontSize(int rank) + { + return (MaximalFontSize * (rank - minimalRank)) / (maximalRank - minimalRank); + } + + private int CalculateWordBoxLength(int length, int fontSize) + { + return (int)Math.Round(length * LengthSizeMultiplier * ((double)fontSize / MaximalFontSize)); + } + private static Size GetImageShift(IList rectangles, int border) { var minX = rectangles.Min(rectangle => rectangle.Left); @@ -37,10 +86,4 @@ private static Size GetImageSize(IList rectangles, int border) return new Size(maxX - minX + 2 * border, maxY - minY + 2 * border); } - - private static Color GetRandomColor() - { - var random = new Random(); - return Color.FromArgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)); - } } \ No newline at end of file diff --git a/TagCloud/CloudDrawer/CustomPalette.cs b/TagCloud/CloudDrawer/CustomPalette.cs new file mode 100644 index 000000000..233b2b764 --- /dev/null +++ b/TagCloud/CloudDrawer/CustomPalette.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace TagCloud.CloudDrawer; + +public class CustomPalette : IPalette +{ + private Color foregroundColor; + private Color backgroundColor; + + public CustomPalette(Color foregroundColor, Color backgroundColor) + { + this.foregroundColor = foregroundColor; + this.backgroundColor = backgroundColor; + } + + public Color ForegroundColor => foregroundColor; + public Color BackgroudColor => backgroundColor; +} \ No newline at end of file diff --git a/TagCloud/CloudDrawer/IDrawer.cs b/TagCloud/CloudDrawer/IDrawer.cs new file mode 100644 index 000000000..c7d119382 --- /dev/null +++ b/TagCloud/CloudDrawer/IDrawer.cs @@ -0,0 +1,9 @@ +using System.Drawing; +using TagCloud.Layouter; + +namespace TagCloud.CloudDrawer; + +public interface IDrawer +{ + Bitmap DrawTagCloud(IEnumerable<(string word, int rank)> words); +} \ No newline at end of file diff --git a/TagCloud/CloudDrawer/IPalette.cs b/TagCloud/CloudDrawer/IPalette.cs new file mode 100644 index 000000000..7705eef07 --- /dev/null +++ b/TagCloud/CloudDrawer/IPalette.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagCloud.CloudDrawer; + +public interface IPalette +{ + Color ForegroundColor { get; } + Color BackgroudColor { get; } +} \ No newline at end of file diff --git a/TagCloud/CloudDrawer/RandomPalette.cs b/TagCloud/CloudDrawer/RandomPalette.cs new file mode 100644 index 000000000..8ad89cd72 --- /dev/null +++ b/TagCloud/CloudDrawer/RandomPalette.cs @@ -0,0 +1,11 @@ +using System.Drawing; + +namespace TagCloud.CloudDrawer; + +public class RandomPalette : IPalette +{ + private Random random = new(); + + public Color ForegroundColor => Color.FromArgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)); + public Color BackgroudColor => Color.White; +} \ No newline at end of file diff --git a/TagCloud/CloudDrawer/Tag.cs b/TagCloud/CloudDrawer/Tag.cs new file mode 100644 index 000000000..c917be99b --- /dev/null +++ b/TagCloud/CloudDrawer/Tag.cs @@ -0,0 +1,17 @@ +using System.Drawing; + +namespace TagCloud.CloudDrawer; + +public class Tag +{ + public string Value; + public Rectangle Position; + public int FontSize; + + public Tag(string value, Rectangle position, int fontSize) + { + Value = value; + Position = position; + FontSize = fontSize; + } +} \ No newline at end of file diff --git a/TagCloud/FileReader/IFileReader.cs b/TagCloud/FileReader/IFileReader.cs index 9d87e706c..1c3ba806a 100644 --- a/TagCloud/FileReader/IFileReader.cs +++ b/TagCloud/FileReader/IFileReader.cs @@ -2,5 +2,5 @@ namespace TagCloud.FileReader; public interface IFileReader { - + IEnumerable ReadLines(string inputPath); } \ No newline at end of file diff --git a/TagCloud/Layouter/ICircularCloudLayouter.cs b/TagCloud/Layouter/ILayouter.cs similarity index 62% rename from TagCloud/Layouter/ICircularCloudLayouter.cs rename to TagCloud/Layouter/ILayouter.cs index 8df434315..a01569b93 100644 --- a/TagCloud/Layouter/ICircularCloudLayouter.cs +++ b/TagCloud/Layouter/ILayouter.cs @@ -2,7 +2,8 @@ namespace TagCloud.Layouter; -public interface ICircularCloudLayouter +public interface ILayouter { + IList Rectangles { get; } Rectangle PutNextRectangle(Size rectangleSize); } \ No newline at end of file diff --git a/TagCloud/Layouter/CircularCloudLayouter.cs b/TagCloud/Layouter/Layouter.cs similarity index 89% rename from TagCloud/Layouter/CircularCloudLayouter.cs rename to TagCloud/Layouter/Layouter.cs index e74d91ed5..d47af314d 100644 --- a/TagCloud/Layouter/CircularCloudLayouter.cs +++ b/TagCloud/Layouter/Layouter.cs @@ -3,12 +3,12 @@ namespace TagCloud.Layouter; -public class CircularCloudLayouter : ICircularCloudLayouter +public class Layouter : ILayouter { private readonly IPointGenerator pointGenerator; public IList Rectangles { get; } - public CircularCloudLayouter(IPointGenerator pointGenerator) + public Layouter(IPointGenerator pointGenerator) { Rectangles = new List(); this.pointGenerator = pointGenerator; diff --git a/TagCloud/PointGenerator/SpiralGenerator.cs b/TagCloud/PointGenerator/SpiralGenerator.cs index 817d2ca43..5eb1c50c6 100644 --- a/TagCloud/PointGenerator/SpiralGenerator.cs +++ b/TagCloud/PointGenerator/SpiralGenerator.cs @@ -9,7 +9,7 @@ public class SpiralGenerator : IPointGenerator private readonly double angleShift; private double currentAngle; - public SpiralGenerator(Point startPoint, int spiralDensity = 1, double angleShift = 0.1) + public SpiralGenerator(Point startPoint, int spiralDensity = 1, double angleShift = 0.01) { if (startPoint.X < 0 || startPoint.Y < 0) throw new ArgumentException("Spiral center point coordinates should be non-negative"); diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 1f6801e35..c3fe050fe 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -1,28 +1,41 @@ using System.Drawing; -using TagCloud.Layouter; +using Autofac; +using TagCloud.CloudDrawer; using TagCloud.PointGenerator; +using CommandLine; +using TagCloud.CloudSaver; +using TagCloud.FileReader; +using TagCloud.Layouter; +using TagCloud.UserInterface; +using TagCloud.WordFilter; +using TagCloud.WordRanker; +using TagCloud.WordsPreprocessor; namespace TagCloud; - public class Program +public class Program +{ + static void Main(string[] args) { - private const int Width = 1920; - private const int Height = 1080; - - static void Main(string[] args) - { - var layouter = new CircularCloudLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2), 1, 0.01)); + var settings = Parser.Default.ParseArguments(args).Value; + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); - var random = new Random(); + builder.RegisterType().As(); - for (var i = 0; i < 150; i++) - { - layouter.PutNextRectangle(new Size(50 + random.Next(0, 100), 50 + random.Next(0, 100))); - } + builder.RegisterType().As(); + builder.Register(l => + new Layouter.Layouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), + settings.CloudDensity))).As(); - var saver = new CloudSaver.CloudSaver(); + builder.Register(s => settings).AsImplementedInterfaces(); - using var bitmap = CloudDrawer.CloudDrawer.DrawTagCloud(layouter.Rectangles); - saver.Save(bitmap, "Sample", "png"); - } - } \ No newline at end of file + var container = builder.Build(); + container.Resolve().Run(settings); + } +} \ No newline at end of file diff --git a/TagCloud/Settings/IAppSettings.cs b/TagCloud/Settings/IAppSettings.cs index b83be67ea..49a040f80 100644 --- a/TagCloud/Settings/IAppSettings.cs +++ b/TagCloud/Settings/IAppSettings.cs @@ -9,4 +9,5 @@ public interface IAppSettings public int CloudWidth { get; } public int CloudHeight { get; } public string LayouterType { get; } + public int CloudDensity { get; } } \ No newline at end of file diff --git a/TagCloud/Settings/Settings.cs b/TagCloud/Settings/Settings.cs index 8b67f0996..ca336da31 100644 --- a/TagCloud/Settings/Settings.cs +++ b/TagCloud/Settings/Settings.cs @@ -4,13 +4,13 @@ namespace TagCloud.Settings; public class Settings : IAppSettings { - [Option('s', "sourceFile", Required = true, HelpText = "Path to file with words to visualize")] + [Option('s', "sourceFile", Default = "text.txt", HelpText = "Path to file with words to visualize")] public string InputPath { get; set; } [Option('o', "outputPath", Default = "result", HelpText = "Path to output image file")] public string OutputPath { get; set; } - [Option('e', "outputFormat", Default = "png", HelpText = "Output image file format")] + [Option('e', "extensionImage", Default = "png", HelpText = "Output image file format")] public string ImageExtension { get; set; } [Option('f', "fontType", Default = "Roboto", HelpText = "Font type of words")] @@ -24,4 +24,7 @@ public class Settings : IAppSettings [Option('l', "layouter", Default = "Circular", HelpText = "Cloud layouter algorithm")] public string LayouterType { get; set; } + + [Option('d', "density", Default = 1, HelpText = "Density of cloud")] + public int CloudDensity { get; set; } } \ No newline at end of file diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj index 79a73a0c4..95be83ce2 100644 --- a/TagCloud/TagCloud.csproj +++ b/TagCloud/TagCloud.csproj @@ -8,6 +8,7 @@ + diff --git a/TagCloud/UserInterface/ConsoleUI.cs b/TagCloud/UserInterface/ConsoleUI.cs index a6137a160..a43ab7043 100644 --- a/TagCloud/UserInterface/ConsoleUI.cs +++ b/TagCloud/UserInterface/ConsoleUI.cs @@ -1,16 +1,39 @@ +using TagCloud.CloudDrawer; +using TagCloud.CloudSaver; +using TagCloud.FileReader; using TagCloud.Settings; +using TagCloud.WordFilter; +using TagCloud.WordRanker; +using TagCloud.WordsPreprocessor; namespace TagCloud.UserInterface; public class ConsoleUI : IUserInterface { - public ConsoleUI() + private readonly IFileReader reader; + private readonly ICloudSaver saver; + private readonly IDrawer drawer; + private readonly IWordRanker ranker; + private readonly IFilter filter; + private readonly IPreprocessor preprocessor; + + public ConsoleUI(IFileReader reader, ICloudSaver saver, IDrawer drawer, IWordRanker ranker, IFilter filter, IPreprocessor preprocessor) { - + this.reader = reader; + this.saver = saver; + this.drawer = drawer; + this.ranker = ranker; + this.filter = filter; + this.preprocessor = preprocessor; } - + public void Run(IAppSettings appSettings) { - throw new NotImplementedException(); + var words = reader.ReadLines(appSettings.InputPath); + var preprocessed = preprocessor.HandleWords(words); + var filtered = filter.FilterWords(preprocessed); + var ranked = ranker.RankWords(filtered); + using var bitmap = drawer.DrawTagCloud(ranked); + saver.Save(bitmap, appSettings.OutputPath, appSettings.ImageExtension); } } \ No newline at end of file diff --git a/TagCloud/WordFilter/IFilter.cs b/TagCloud/WordFilter/IFilter.cs new file mode 100644 index 000000000..d3cabd7a5 --- /dev/null +++ b/TagCloud/WordFilter/IFilter.cs @@ -0,0 +1,8 @@ +namespace TagCloud.WordFilter; + +public interface IFilter +{ + IEnumerable FilterWords(IEnumerable words); + + IFilter UsingFilter(Func filter); +} \ No newline at end of file diff --git a/TagCloud/WordFilter/WordFilter.cs b/TagCloud/WordFilter/WordFilter.cs new file mode 100644 index 000000000..a2683d469 --- /dev/null +++ b/TagCloud/WordFilter/WordFilter.cs @@ -0,0 +1,18 @@ +namespace TagCloud.WordFilter; + +public class WordFilter : IFilter +{ + private readonly List> filters = []; + + public IEnumerable FilterWords(IEnumerable words) + { + return words.Where(word => filters.All(f => f(word))); + } + + public IFilter UsingFilter(Func filter) + { + filters.Add(filter); + + return this; + } +} \ No newline at end of file diff --git a/TagCloud/WordHandler/WordRankerByFrequency.cs b/TagCloud/WordHandler/WordRankerByFrequency.cs deleted file mode 100644 index d687b32ee..000000000 --- a/TagCloud/WordHandler/WordRankerByFrequency.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TagCloud.WordHandler; - -public class WordRankerByFrequency : IWordHandler -{ - public IEnumerable<(string word, int rank)> RankWords(IEnumerable words) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/TagCloud/WordPreprocessor/DefaultPreprocessor.cs b/TagCloud/WordPreprocessor/DefaultPreprocessor.cs deleted file mode 100644 index 36c5113cd..000000000 --- a/TagCloud/WordPreprocessor/DefaultPreprocessor.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TagCloud.WordPreprocessor; - -public class DefaultPreprocessor : IWordPreprocessor -{ - public IEnumerable HandleWords(IEnumerable words) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/TagCloud/WordPreprocessor/IWordPreprocessor.cs b/TagCloud/WordPreprocessor/IWordPreprocessor.cs deleted file mode 100644 index f562dcbe3..000000000 --- a/TagCloud/WordPreprocessor/IWordPreprocessor.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace TagCloud.WordPreprocessor; - -public interface IWordPreprocessor -{ - IEnumerable HandleWords(IEnumerable words); -} \ No newline at end of file diff --git a/TagCloud/WordHandler/IWordHandler.cs b/TagCloud/WordRanker/IWordRanker.cs similarity index 57% rename from TagCloud/WordHandler/IWordHandler.cs rename to TagCloud/WordRanker/IWordRanker.cs index 30798e1f8..d86b8cfa3 100644 --- a/TagCloud/WordHandler/IWordHandler.cs +++ b/TagCloud/WordRanker/IWordRanker.cs @@ -1,6 +1,6 @@ -namespace TagCloud.WordHandler; +namespace TagCloud.WordRanker; -public interface IWordHandler +public interface IWordRanker { IEnumerable<(string word, int rank)> RankWords(IEnumerable words); } \ No newline at end of file diff --git a/TagCloud/WordRanker/WordRankerByFrequency.cs b/TagCloud/WordRanker/WordRankerByFrequency.cs new file mode 100644 index 000000000..15488d6ec --- /dev/null +++ b/TagCloud/WordRanker/WordRankerByFrequency.cs @@ -0,0 +1,10 @@ +namespace TagCloud.WordRanker; + +public class WordRankerByFrequency : IWordRanker +{ + public IEnumerable<(string word, int rank)> RankWords(IEnumerable words) + { + return words.GroupBy(word => word.Trim().ToLowerInvariant()).OrderByDescending(g => g.Count()).ToList() + .Select(g => ValueTuple.Create(g.Key, g.Count())); + } +} \ No newline at end of file diff --git a/TagCloud/WordsPreprocessor/DefaultPreprocessor.cs b/TagCloud/WordsPreprocessor/DefaultPreprocessor.cs new file mode 100644 index 000000000..35732260b --- /dev/null +++ b/TagCloud/WordsPreprocessor/DefaultPreprocessor.cs @@ -0,0 +1,9 @@ +namespace TagCloud.WordsPreprocessor; + +public class DefaultPreprocessor : IPreprocessor +{ + public IEnumerable HandleWords(IEnumerable words) + { + return words.Select(word => word.ToLower()); + } +} \ No newline at end of file diff --git a/TagCloud/WordsPreprocessor/IPreprocessor.cs b/TagCloud/WordsPreprocessor/IPreprocessor.cs new file mode 100644 index 000000000..28f70b4cf --- /dev/null +++ b/TagCloud/WordsPreprocessor/IPreprocessor.cs @@ -0,0 +1,6 @@ +namespace TagCloud.WordsPreprocessor; + +public interface IPreprocessor +{ + IEnumerable HandleWords(IEnumerable words); +} \ No newline at end of file diff --git a/TagCloud/result.png b/TagCloud/result.png new file mode 100644 index 0000000000000000000000000000000000000000..063517bfacc1702cc03c169bb0c95ae38345a46d GIT binary patch literal 37179 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 B&@uo3 literal 0 HcmV?d00001 diff --git a/TagCloud/text.txt b/TagCloud/text.txt new file mode 100644 index 000000000..d3cf4c5ac --- /dev/null +++ b/TagCloud/text.txt @@ -0,0 +1,320 @@ +Lorem +ipsum +dolor +sit +amet +consectetuer +adipiscing +elit +Phasellus +hendrerit +Pellentesque +aliquet +nibh +nec +urna +In +nisi +neque +aliquet +vel +dapibus +id +mattis +vel +nisi +Sed +pretium +ligula +sollicitudin +laoreet +viverra +tortor +libero +sodales +leo +eget +blandit +nunc +tortor +eu +nibh +Nullam +mollis +Ut +justo +Suspendisse +potenti +Sed +egestas +ante +et +vulputate +volutpat +eros +pede +semper +est +vitae +luctus +metus +libero +eu +augue +Morbi +purus +libero +faucibus +adipiscing +commodo +quis +gravida +id +est +Sed +lectus +Praesent +elementum +hendrerit +tortor +Sed +semper +lorem +at +felis +Vestibulum +volutpat +lacus +a +ultrices +sagittis +mi +neque +euismod +dui +eu +pulvinar +nunc +sapien +ornare +nisl +Phasellus +pede +arcu +dapibus +eu +fermentum +et +dapibus +sed +urna +Morbi +interdum +mollis +sapien +Sed +ac +risus +Phasellus +lacinia +magna +a +ullamcorper +laoreet +lectus +arcu +pulvinar +risus +vitae +facilisis +libero +dolor +a +purus +Sed +vel +lacus +Mauris +nibh +felis +adipiscing +varius +adipiscing +in +lacinia +vel +tellus +Suspendisse +ac +urna +Etiam +pellentesque +mauris +ut +lectus +Nunc +tellus +ante +mattis +eget +gravida +vitae +ultricies +ac +leo +Integer +leo +pede +ornare +a +lacinia +eu +vulputate +vel +nisl +Suspendisse +mauris +Fusce +accumsan +mollis +eros +Pellentesque +a +diam +sit +amet +mi +ullamcorper +vehicula +Integer +adipiscing +risus +a +sem +Nullam +quis +massa +sit +amet +nibh +viverra +malesuada +Nunc +sem +lacus +accumsan +quis +faucibus +non +congue +vel +arcu +Ut +scelerisque +hendrerit +tellus +Integer +sagittis +Vivamus +a +mauris +eget +arcu +gravida +tristique +Nunc +iaculis +mi +in +ante +Vivamus +imperdiet +nibh +feugiat +est +Ut +convallis +sem +sit +amet +interdum +consectetuer +odio +augue +aliquam +leo +nec +dapibus +tortor +nibh +sed +augue +Integer +eu +magna +sit +amet +metus +fermentum +posuere +Morbi +sit +amet +nulla +sed +dolor +elementum +imperdiet +Quisque +fermentum +Cum +sociis +natoque +penatibus +et +magnis +xdis +parturient +montes +nascetur +ridiculus +mus +Pellentesque +adipiscing +eros +ut +libero +Ut +condimentum +mi +vel +tellus +Suspendisse +laoreet +Fusce +ut +est +sed +dolor +gravida +convallis +Morbi +vitae +ante +Vivamus +ultrices +luctus +nunc +Suspendisse +et +dolor +Etiam +dignissim +Proin +malesuada +adipiscing +lacus +Donec +metus +Curabitur +gravida diff --git a/TagCloudTests/ConsoleUI_Should.cs b/TagCloudTests/ConsoleUI_Should.cs new file mode 100644 index 000000000..2ec83dc91 --- /dev/null +++ b/TagCloudTests/ConsoleUI_Should.cs @@ -0,0 +1,65 @@ +using Autofac; +using CommandLine; +using TagCloud.CloudDrawer; +using TagCloud.CloudSaver; +using TagCloud.FileReader; +using TagCloud.Layouter; +using TagCloud.PointGenerator; +using TagCloud.Settings; +using TagCloud.UserInterface; +using TagCloud.WordFilter; +using TagCloud.WordRanker; +using TagCloud.WordsPreprocessor; + +namespace TagCloudTests; + +[TestFixture] +public class ConsoleUI_Should +{ + private IAppSettings settings; + private IUserInterface sut; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + settings = Parser.Default.ParseArguments(new List()).Value; + + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.Register(l => + new Layouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), + settings.CloudDensity))).As(); + + builder.Register(s => settings).AsImplementedInterfaces(); + + var container = builder.Build(); + sut = container.Resolve(); + } + + [Test] + public void GenerateFileWithCorrectSettings() + { + sut.Run(settings); + + File.Exists($"{settings.OutputPath}.{settings.ImageExtension}").Should().BeTrue(); + + File.Delete($"{settings.OutputPath}.{settings.ImageExtension}"); + } + + private class FakeReader : IFileReader + { + public IEnumerable ReadLines(string inputPath) + { + yield return "test"; + } + } +} \ No newline at end of file diff --git a/TagCloudTests/IOTests/CloudSaver_Should.cs b/TagCloudTests/IOTests/CloudSaver_Should.cs new file mode 100644 index 000000000..a1fac29de --- /dev/null +++ b/TagCloudTests/IOTests/CloudSaver_Should.cs @@ -0,0 +1,25 @@ +using NUnit.Framework.Internal; +using TagCloud.CloudSaver; + +namespace TagCloudTests; + +[TestFixture] +public class CloudSaver_Should +{ + private ICloudSaver sut = new CloudSaver(); + private const string filename = "test"; + private const string extension = "png"; + + + [Test] + public void SaveBitmapToFile() + { + using var bitmap = new Bitmap(50, 50); + + sut.Save(bitmap, filename, extension); + + File.Exists($"{filename}.{extension}").Should().BeTrue(); + + File.Delete($"{filename}.{extension}"); + } +} \ No newline at end of file diff --git a/TagCloudTests/IOTests/FileReader_Should.cs b/TagCloudTests/IOTests/FileReader_Should.cs new file mode 100644 index 000000000..8fdcc947d --- /dev/null +++ b/TagCloudTests/IOTests/FileReader_Should.cs @@ -0,0 +1,33 @@ +using Microsoft.VisualStudio.TestPlatform.Utilities; +using TagCloud.FileReader; + +namespace TagCloudTests; + +[TestFixture] +public class FileReader_Should +{ + private IFileReader sut = new FileReader(); + private const string inputPath = "test.txt"; + private string text = $"one{Environment.NewLine}two{Environment.NewLine}three{Environment.NewLine}"; + + [SetUp] + public void SetUp() + { + using var fileStream = File.Open(inputPath, FileMode.Create); + using var writer = new StreamWriter(fileStream); + + writer.Write(text); + } + + [Test] + public void ReadWordsFromTxt() + { + var expected = text.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).ToList(); + + var result = sut.ReadLines(inputPath); + + result.Should().BeEquivalentTo(expected); + + File.Delete(inputPath); + } +} \ No newline at end of file diff --git a/TagCloudTests/CircularCloudLayouter_Should.cs b/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs similarity index 68% rename from TagCloudTests/CircularCloudLayouter_Should.cs rename to TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs index 939c1fac4..c89600a6e 100644 --- a/TagCloudTests/CircularCloudLayouter_Should.cs +++ b/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs @@ -8,37 +8,13 @@ public class CircularCloudLayouter_Should { private const int Width = 1920; private const int Height = 1080; - private CircularCloudLayouter sut; + private Layouter sut; [SetUp] public void Setup() { - sut = new CircularCloudLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); - } - - [TearDown] - public void TearDown() - { - if (TestContext.CurrentContext.Result.Outcome == ResultState.Failure) - { - var bitmap = CloudDrawer.DrawTagCloud(sut.Rectangles); - - var path = @$"{Environment.CurrentDirectory}\..\..\..\FailedTests\{this.GetType()}"; - var absPath = Path.GetFullPath(path); - - if (!Directory.Exists(absPath)) - { - Directory.CreateDirectory(absPath); - } - - var fileName = TestContext.CurrentContext.Test.Name; - absPath += @$"\{fileName}.png"; - - bitmap.Save(absPath); - - TestContext.Out.WriteLine($"Tag cloud visualization saved to file <{absPath}>"); - } + sut = new Layouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); } [Test] diff --git a/TagCloudTests/SpiralGenerator_Should.cs b/TagCloudTests/LayouterTests/SpiralGenerator_Should.cs similarity index 95% rename from TagCloudTests/SpiralGenerator_Should.cs rename to TagCloudTests/LayouterTests/SpiralGenerator_Should.cs index dc98eae2a..146e78bfb 100644 --- a/TagCloudTests/SpiralGenerator_Should.cs +++ b/TagCloudTests/LayouterTests/SpiralGenerator_Should.cs @@ -12,7 +12,7 @@ public class SpiralGenerator_Should public void Setup() { startPoint = new Point(Width / 2, Height / 2); - sut = new SpiralGenerator(startPoint); + sut = new SpiralGenerator(startPoint, 1, 0.1); } [Test] diff --git a/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs b/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs new file mode 100644 index 000000000..53ddeeee7 --- /dev/null +++ b/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs @@ -0,0 +1,33 @@ +using TagCloud.WordsPreprocessor; + +namespace TagCloudTests.WordProcessorsTests; + +[TestFixture] +public class DefaultPreprocessor_Should +{ + private IPreprocessor sut = new DefaultPreprocessor(); + private List words = new(){"Aba", "caBC", "teSTword", "ZAZ", "word"}; + + [SetUp] + public void SetUp() + { + sut = new DefaultPreprocessor(); + } + + [Test] + public void ConvertAllWordsToLowerCase() + { + var expected = words.Select(w => w.ToLower()); + var result = sut.HandleWords(words); + + result.Should().BeEquivalentTo(expected); + } + + [Test] + public void ReturnEmpty_WhenNoWordsGiven() + { + var result = sut.HandleWords(new List()); + + result.Should().BeEmpty(); + } +} \ No newline at end of file diff --git a/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs b/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs new file mode 100644 index 000000000..dd5d92fbc --- /dev/null +++ b/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs @@ -0,0 +1,46 @@ +using TagCloud.WordFilter; + +namespace TagCloudTests.WordProcessorsTests; + +[TestFixture] +public class WordFilter_Should +{ + private IFilter sut; + private List words = new(){"aba", "cabc", "testword", "zaz"}; + + [SetUp] + public void SetUp() + { + sut = new WordFilter(); + } + + [Test] + public void FilterWordList_ByGivenFilter() + { + sut = sut.UsingFilter(w => w.Length > 3); + + var result = sut.FilterWords(words); + + result.Should().Contain(new List {"cabc", "testword"}); + } + + [Test] + public void ReturnSameWords_WhenFilterIsUseless() + { + sut = sut.UsingFilter(w => true); + + var result = sut.FilterWords(words); + + result.Should().BeEquivalentTo(words); + } + + [Test] + public void ShouldReturnEmpty_WhenFilterFiltersAllWords() + { + sut = sut.UsingFilter(w => false); + + var result = sut.FilterWords(words); + + result.Should().BeEmpty(); + } +} \ No newline at end of file diff --git a/TagCloudTests/WordProcessorsTests/WordRanker_Should.cs b/TagCloudTests/WordProcessorsTests/WordRanker_Should.cs new file mode 100644 index 000000000..c009009c9 --- /dev/null +++ b/TagCloudTests/WordProcessorsTests/WordRanker_Should.cs @@ -0,0 +1,34 @@ +using TagCloud.WordRanker; + +namespace TagCloudTests.WordProcessorsTests; + +[TestFixture] +public class WordRanker_Should +{ + private IWordRanker sut; + private List words = new() { "aba", "caba", "aba", "a", "a", "a" }; + + [SetUp] + public void SetUp() + { + sut = new WordRankerByFrequency(); + } + + [Test] + public void CorrectlyRankWords() + { + var expected = words.GroupBy(word => word.Trim().ToLowerInvariant()).OrderByDescending(g => g.Count()).ToList() + .Select(g => ValueTuple.Create(g.Key, g.Count())); + var result = sut.RankWords(words); + + result.Should().BeEquivalentTo(expected); + } + + [Test] + public void ReturnEmpty_WhenNowordsGiven() + { + var result = sut.RankWords(new List()); + + result.Should().BeEmpty(); + } +} \ No newline at end of file From 8b64b0da5780e83b3d0770b0a1d0d56fa5e8d148 Mon Sep 17 00:00:00 2001 From: HardreaM Date: Tue, 23 Jan 2024 23:40:09 +0500 Subject: [PATCH 4/6] Refactoring; Change settings configuration --- .../{Settings => AppSettings}/IAppSettings.cs | 6 +- .../{Settings => AppSettings}/Settings.cs | 30 +++++--- TagCloud/CloudSaver/CloudSaver.cs | 14 ---- TagCloud/Configurator.cs | 34 +++++++++ .../{CloudDrawer => Drawer}/CloudDrawer.cs | 31 +++------ .../{CloudDrawer => Drawer}/CustomPalette.cs | 4 +- TagCloud/{CloudDrawer => Drawer}/IDrawer.cs | 3 +- TagCloud/{CloudDrawer => Drawer}/IPalette.cs | 2 +- .../{CloudDrawer => Drawer}/RandomPalette.cs | 4 +- TagCloud/{CloudDrawer => Drawer}/Tag.cs | 2 +- .../{FileReader.cs => TxtReader.cs} | 2 +- .../ICloudSaver.cs => FileSaver/ISaver.cs} | 4 +- TagCloud/FileSaver/ImageSaver.cs | 18 +++++ TagCloud/{WordFilter => Filter}/IFilter.cs | 2 +- TagCloud/{WordFilter => Filter}/WordFilter.cs | 6 +- .../{Layouter.cs => CircularLayouter.cs} | 4 +- TagCloud/Program.cs | 26 ++++--- TagCloud/UserInterface/ConsoleUI.cs | 13 ++-- TagCloud/UserInterface/IUserInterface.cs | 2 +- TagCloud/result.png | Bin 37179 -> 46512 bytes TagCloud/text.txt | 65 ++++++++++++++++++ TagCloudTests/ConsoleUI_Should.cs | 12 ++-- TagCloudTests/IOTests/CloudSaver_Should.cs | 10 +-- TagCloudTests/IOTests/FileReader_Should.cs | 8 +-- .../CircularCloudLayouter_Should.cs | 5 +- .../DefaultPreprocessor_Should.cs | 2 +- .../WordProcessorsTests/WordFilter_Should.cs | 8 +-- 27 files changed, 211 insertions(+), 106 deletions(-) rename TagCloud/{Settings => AppSettings}/IAppSettings.cs (62%) rename TagCloud/{Settings => AppSettings}/Settings.cs (60%) delete mode 100644 TagCloud/CloudSaver/CloudSaver.cs create mode 100644 TagCloud/Configurator.cs rename TagCloud/{CloudDrawer => Drawer}/CloudDrawer.cs (72%) rename TagCloud/{CloudDrawer => Drawer}/CustomPalette.cs (91%) rename TagCloud/{CloudDrawer => Drawer}/IDrawer.cs (67%) rename TagCloud/{CloudDrawer => Drawer}/IPalette.cs (79%) rename TagCloud/{CloudDrawer => Drawer}/RandomPalette.cs (87%) rename TagCloud/{CloudDrawer => Drawer}/Tag.cs (89%) rename TagCloud/FileReader/{FileReader.cs => TxtReader.cs} (87%) rename TagCloud/{CloudSaver/ICloudSaver.cs => FileSaver/ISaver.cs} (61%) create mode 100644 TagCloud/FileSaver/ImageSaver.cs rename TagCloud/{WordFilter => Filter}/IFilter.cs (82%) rename TagCloud/{WordFilter => Filter}/WordFilter.cs (88%) rename TagCloud/Layouter/{Layouter.cs => CircularLayouter.cs} (90%) diff --git a/TagCloud/Settings/IAppSettings.cs b/TagCloud/AppSettings/IAppSettings.cs similarity index 62% rename from TagCloud/Settings/IAppSettings.cs rename to TagCloud/AppSettings/IAppSettings.cs index 49a040f80..f9b922a22 100644 --- a/TagCloud/Settings/IAppSettings.cs +++ b/TagCloud/AppSettings/IAppSettings.cs @@ -1,4 +1,4 @@ -namespace TagCloud.Settings; +namespace TagCloud.AppSettings; public interface IAppSettings { @@ -10,4 +10,8 @@ public interface IAppSettings public int CloudHeight { get; } public string LayouterType { get; } public int CloudDensity { get; } + public bool UseRandomPalette { get; } + public string BackgroundColor { get; } + public string ForegroundColor { get; } + public string BoringWordsFile { get; } } \ No newline at end of file diff --git a/TagCloud/Settings/Settings.cs b/TagCloud/AppSettings/Settings.cs similarity index 60% rename from TagCloud/Settings/Settings.cs rename to TagCloud/AppSettings/Settings.cs index ca336da31..5ef797d47 100644 --- a/TagCloud/Settings/Settings.cs +++ b/TagCloud/AppSettings/Settings.cs @@ -1,30 +1,42 @@ using CommandLine; -namespace TagCloud.Settings; +namespace TagCloud.AppSettings; public class Settings : IAppSettings { [Option('s', "sourceFile", Default = "text.txt", HelpText = "Path to file with words to visualize")] public string InputPath { get; set; } - + [Option('o', "outputPath", Default = "result", HelpText = "Path to output image file")] public string OutputPath { get; set; } - + [Option('e', "extensionImage", Default = "png", HelpText = "Output image file format")] public string ImageExtension { get; set; } - - [Option('f', "fontType", Default = "Roboto", HelpText = "Font type of words")] + + [Option('f', "fontType", Default = "SansSerif", HelpText = "Font type of words")] public string FontType { get; set; } - + [Option('W', "width", Default = 1920, HelpText = "Width of cloud")] public int CloudWidth { get; set; } - + [Option('H', "height", Default = 1080, HelpText = "Height of cloud")] public int CloudHeight { get; set; } - + [Option('l', "layouter", Default = "Circular", HelpText = "Cloud layouter algorithm")] public string LayouterType { get; set; } - + [Option('d', "density", Default = 1, HelpText = "Density of cloud")] public int CloudDensity { get; set; } + + [Option('r', "randomPalette", Default = true, HelpText = "Use random colors")] + public bool UseRandomPalette { get; set; } + + [Option("background", Default = "White", HelpText = "Cloud layouter algorithm")] + public string BackgroundColor { get; set; } + + [Option("foreground", Default = "Black", HelpText = "Cloud layouter algorithm")] + public string ForegroundColor { get; set; } + + [Option("boringWordsFile", Default = null, HelpText = "Cloud layouter algorithm")] + public string BoringWordsFile { get; set; } } \ No newline at end of file diff --git a/TagCloud/CloudSaver/CloudSaver.cs b/TagCloud/CloudSaver/CloudSaver.cs deleted file mode 100644 index 3a28b34f3..000000000 --- a/TagCloud/CloudSaver/CloudSaver.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Drawing; - -namespace TagCloud.CloudSaver; - -public class CloudSaver : ICloudSaver -{ - public void Save(Bitmap bitmap, string outputPath, string imageFormat) - { - using (bitmap) - { - bitmap.Save($"{outputPath}.{imageFormat}"); - } - } -} \ No newline at end of file diff --git a/TagCloud/Configurator.cs b/TagCloud/Configurator.cs new file mode 100644 index 000000000..6dc88aad1 --- /dev/null +++ b/TagCloud/Configurator.cs @@ -0,0 +1,34 @@ +using System.Drawing; +using Autofac; +using CommandLine; +using TagCloud.AppSettings; +using TagCloud.Drawer; +using TagCloud.FileReader; +using TagCloud.Filter; + +namespace TagCloud; + +public class Configurator +{ + public static IAppSettings Parse(string[] args, ContainerBuilder builder) + { + var settings = Parser.Default.ParseArguments(args).WithParsed(o => + { + if (o.UseRandomPalette) + builder.RegisterType().As(); + else + builder.Register(p => + new CustomPalette(Color.FromName(o.ForegroundColor), Color.FromName(o.BackgroundColor))); + var filter = new WordFilter().UsingFilter((word) => word.Length > 3); + if (string.IsNullOrEmpty(o.BoringWordsFile)) + builder.Register(c => filter).As(); + else + { + var boringWords = new TxtReader().ReadLines(o.BoringWordsFile); + builder.Register(c => filter.UsingFilter((word) => !boringWords.Contains(word))); + } + }); + + return settings.Value; + } +} \ No newline at end of file diff --git a/TagCloud/CloudDrawer/CloudDrawer.cs b/TagCloud/Drawer/CloudDrawer.cs similarity index 72% rename from TagCloud/CloudDrawer/CloudDrawer.cs rename to TagCloud/Drawer/CloudDrawer.cs index 4ef86a369..976283b84 100644 --- a/TagCloud/CloudDrawer/CloudDrawer.cs +++ b/TagCloud/Drawer/CloudDrawer.cs @@ -1,8 +1,8 @@ using System.Drawing; +using TagCloud.AppSettings; using TagCloud.Layouter; -using TagCloud.Settings; -namespace TagCloud.CloudDrawer; +namespace TagCloud.Drawer; public class CloudDrawer : IDrawer { @@ -13,7 +13,6 @@ public class CloudDrawer : IDrawer private int maximalRank; private const int MaximalFontSize = 50; private const int LengthSizeMultiplier = 35; - private const int ImageBorder = 10; public CloudDrawer(ILayouter layouter, IPalette palette, IAppSettings appSettings) { @@ -21,12 +20,12 @@ public CloudDrawer(ILayouter layouter, IPalette palette, IAppSettings appSetting this.palette = palette; this.appSettings = appSettings; } - + public Bitmap DrawTagCloud(IEnumerable<(string word, int rank)> words) { var tags = PlaceWords(words); - var imageSize = GetImageSize(layouter.Rectangles, ImageBorder); - var shift = GetImageShift(layouter.Rectangles, ImageBorder); + var imageSize = new Size(appSettings.CloudWidth, appSettings.CloudHeight); + var shift = GetImageShift(layouter.Rectangles); var image = new Bitmap(imageSize.Width, imageSize.Height); using var graphics = Graphics.FromImage(image); using var background = new SolidBrush(palette.BackgroudColor); @@ -35,7 +34,7 @@ public Bitmap DrawTagCloud(IEnumerable<(string word, int rank)> words) { var shiftedCoordinates = new PointF(tag.Position.X - shift.Width, tag.Position.Y - shift.Height); using var brush = new SolidBrush(palette.ForegroundColor); - graphics.DrawString(tag.Value, new Font(FontFamily.GenericSerif, tag.FontSize), brush, shiftedCoordinates); + graphics.DrawString(tag.Value, new Font(appSettings.FontType, tag.FontSize), brush, shiftedCoordinates); } return image; @@ -45,9 +44,9 @@ private IList PlaceWords(IEnumerable<(string word, int rank)> words) { maximalRank = words.First().rank; minimalRank = words.Last().rank - 1; - + var tags = new List(); - + foreach (var pair in words) { var fontSize = CalculateFontSize(pair.rank); @@ -69,21 +68,11 @@ private int CalculateWordBoxLength(int length, int fontSize) return (int)Math.Round(length * LengthSizeMultiplier * ((double)fontSize / MaximalFontSize)); } - private static Size GetImageShift(IList rectangles, int border) - { - var minX = rectangles.Min(rectangle => rectangle.Left); - var minY = rectangles.Min(rectangle => rectangle.Top); - - return new Size(minX - border, minY - border); - } - - private static Size GetImageSize(IList rectangles, int border) + private static Size GetImageShift(IList rectangles) { var minX = rectangles.Min(rectangle => rectangle.Left); - var maxX = rectangles.Max(rectangle => rectangle.Right); var minY = rectangles.Min(rectangle => rectangle.Top); - var maxY = rectangles.Max(rectangle => rectangle.Bottom); - return new Size(maxX - minX + 2 * border, maxY - minY + 2 * border); + return new Size(minX, minY); } } \ No newline at end of file diff --git a/TagCloud/CloudDrawer/CustomPalette.cs b/TagCloud/Drawer/CustomPalette.cs similarity index 91% rename from TagCloud/CloudDrawer/CustomPalette.cs rename to TagCloud/Drawer/CustomPalette.cs index 233b2b764..897a27399 100644 --- a/TagCloud/CloudDrawer/CustomPalette.cs +++ b/TagCloud/Drawer/CustomPalette.cs @@ -1,12 +1,12 @@ using System.Drawing; -namespace TagCloud.CloudDrawer; +namespace TagCloud.Drawer; public class CustomPalette : IPalette { private Color foregroundColor; private Color backgroundColor; - + public CustomPalette(Color foregroundColor, Color backgroundColor) { this.foregroundColor = foregroundColor; diff --git a/TagCloud/CloudDrawer/IDrawer.cs b/TagCloud/Drawer/IDrawer.cs similarity index 67% rename from TagCloud/CloudDrawer/IDrawer.cs rename to TagCloud/Drawer/IDrawer.cs index c7d119382..6524cf758 100644 --- a/TagCloud/CloudDrawer/IDrawer.cs +++ b/TagCloud/Drawer/IDrawer.cs @@ -1,7 +1,6 @@ using System.Drawing; -using TagCloud.Layouter; -namespace TagCloud.CloudDrawer; +namespace TagCloud.Drawer; public interface IDrawer { diff --git a/TagCloud/CloudDrawer/IPalette.cs b/TagCloud/Drawer/IPalette.cs similarity index 79% rename from TagCloud/CloudDrawer/IPalette.cs rename to TagCloud/Drawer/IPalette.cs index 7705eef07..91896f82e 100644 --- a/TagCloud/CloudDrawer/IPalette.cs +++ b/TagCloud/Drawer/IPalette.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloud.CloudDrawer; +namespace TagCloud.Drawer; public interface IPalette { diff --git a/TagCloud/CloudDrawer/RandomPalette.cs b/TagCloud/Drawer/RandomPalette.cs similarity index 87% rename from TagCloud/CloudDrawer/RandomPalette.cs rename to TagCloud/Drawer/RandomPalette.cs index 8ad89cd72..5b39a2fd9 100644 --- a/TagCloud/CloudDrawer/RandomPalette.cs +++ b/TagCloud/Drawer/RandomPalette.cs @@ -1,11 +1,11 @@ using System.Drawing; -namespace TagCloud.CloudDrawer; +namespace TagCloud.Drawer; public class RandomPalette : IPalette { private Random random = new(); - + public Color ForegroundColor => Color.FromArgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)); public Color BackgroudColor => Color.White; } \ No newline at end of file diff --git a/TagCloud/CloudDrawer/Tag.cs b/TagCloud/Drawer/Tag.cs similarity index 89% rename from TagCloud/CloudDrawer/Tag.cs rename to TagCloud/Drawer/Tag.cs index c917be99b..095a48fa6 100644 --- a/TagCloud/CloudDrawer/Tag.cs +++ b/TagCloud/Drawer/Tag.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloud.CloudDrawer; +namespace TagCloud.Drawer; public class Tag { diff --git a/TagCloud/FileReader/FileReader.cs b/TagCloud/FileReader/TxtReader.cs similarity index 87% rename from TagCloud/FileReader/FileReader.cs rename to TagCloud/FileReader/TxtReader.cs index bc8fc6c26..63395bf0f 100644 --- a/TagCloud/FileReader/FileReader.cs +++ b/TagCloud/FileReader/TxtReader.cs @@ -1,6 +1,6 @@ namespace TagCloud.FileReader; -public class FileReader : IFileReader +public class TxtReader : IFileReader { public IEnumerable ReadLines(string InputPath) { diff --git a/TagCloud/CloudSaver/ICloudSaver.cs b/TagCloud/FileSaver/ISaver.cs similarity index 61% rename from TagCloud/CloudSaver/ICloudSaver.cs rename to TagCloud/FileSaver/ISaver.cs index 16342e39e..34344ad06 100644 --- a/TagCloud/CloudSaver/ICloudSaver.cs +++ b/TagCloud/FileSaver/ISaver.cs @@ -1,8 +1,8 @@ using System.Drawing; -namespace TagCloud.CloudSaver; +namespace TagCloud.FileSaver; -public interface ICloudSaver +public interface ISaver { void Save(Bitmap bitmap, string OutputPath, string imageFormat); } \ No newline at end of file diff --git a/TagCloud/FileSaver/ImageSaver.cs b/TagCloud/FileSaver/ImageSaver.cs new file mode 100644 index 000000000..8488e4503 --- /dev/null +++ b/TagCloud/FileSaver/ImageSaver.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace TagCloud.FileSaver; + +public class ImageSaver : ISaver +{ + private List supportedFormats = new() { "png", "jpg", "jpeg", "bmp", "gif" }; + + public void Save(Bitmap bitmap, string outputPath, string imageFormat) + { + if (!supportedFormats.Contains(imageFormat)) + throw new ArgumentException($"{imageFormat} format is not supported"); + using (bitmap) + { + bitmap.Save($"{outputPath}.{imageFormat}"); + } + } +} \ No newline at end of file diff --git a/TagCloud/WordFilter/IFilter.cs b/TagCloud/Filter/IFilter.cs similarity index 82% rename from TagCloud/WordFilter/IFilter.cs rename to TagCloud/Filter/IFilter.cs index d3cabd7a5..21aa1c12c 100644 --- a/TagCloud/WordFilter/IFilter.cs +++ b/TagCloud/Filter/IFilter.cs @@ -1,4 +1,4 @@ -namespace TagCloud.WordFilter; +namespace TagCloud.Filter; public interface IFilter { diff --git a/TagCloud/WordFilter/WordFilter.cs b/TagCloud/Filter/WordFilter.cs similarity index 88% rename from TagCloud/WordFilter/WordFilter.cs rename to TagCloud/Filter/WordFilter.cs index a2683d469..1b5cd5201 100644 --- a/TagCloud/WordFilter/WordFilter.cs +++ b/TagCloud/Filter/WordFilter.cs @@ -1,9 +1,9 @@ -namespace TagCloud.WordFilter; +namespace TagCloud.Filter; public class WordFilter : IFilter { private readonly List> filters = []; - + public IEnumerable FilterWords(IEnumerable words) { return words.Where(word => filters.All(f => f(word))); @@ -12,7 +12,7 @@ public IEnumerable FilterWords(IEnumerable words) public IFilter UsingFilter(Func filter) { filters.Add(filter); - + return this; } } \ No newline at end of file diff --git a/TagCloud/Layouter/Layouter.cs b/TagCloud/Layouter/CircularLayouter.cs similarity index 90% rename from TagCloud/Layouter/Layouter.cs rename to TagCloud/Layouter/CircularLayouter.cs index d47af314d..9a5e0a6d3 100644 --- a/TagCloud/Layouter/Layouter.cs +++ b/TagCloud/Layouter/CircularLayouter.cs @@ -3,12 +3,12 @@ namespace TagCloud.Layouter; -public class Layouter : ILayouter +public class CircularLayouter : ILayouter { private readonly IPointGenerator pointGenerator; public IList Rectangles { get; } - public Layouter(IPointGenerator pointGenerator) + public CircularLayouter(IPointGenerator pointGenerator) { Rectangles = new List(); this.pointGenerator = pointGenerator; diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index c3fe050fe..84549432f 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -1,13 +1,12 @@ using System.Drawing; using Autofac; -using TagCloud.CloudDrawer; -using TagCloud.PointGenerator; -using CommandLine; -using TagCloud.CloudSaver; +using TagCloud.Drawer; using TagCloud.FileReader; +using TagCloud.FileSaver; +using TagCloud.Filter; using TagCloud.Layouter; +using TagCloud.PointGenerator; using TagCloud.UserInterface; -using TagCloud.WordFilter; using TagCloud.WordRanker; using TagCloud.WordsPreprocessor; @@ -17,23 +16,22 @@ public class Program { static void Main(string[] args) { - var settings = Parser.Default.ParseArguments(args).Value; var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); + var settings = Configurator.Parse(args, builder); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); - builder.Register(l => - new Layouter.Layouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), + builder.Register(c => new WordFilter().UsingFilter((word) => word.Length > 3)).As(); + builder.Register(c => + new CircularLayouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), settings.CloudDensity))).As(); - builder.Register(s => settings).AsImplementedInterfaces(); + builder.Register(c => settings).AsImplementedInterfaces(); var container = builder.Build(); container.Resolve().Run(settings); diff --git a/TagCloud/UserInterface/ConsoleUI.cs b/TagCloud/UserInterface/ConsoleUI.cs index a43ab7043..f3f927441 100644 --- a/TagCloud/UserInterface/ConsoleUI.cs +++ b/TagCloud/UserInterface/ConsoleUI.cs @@ -1,8 +1,8 @@ -using TagCloud.CloudDrawer; -using TagCloud.CloudSaver; +using TagCloud.AppSettings; +using TagCloud.Drawer; using TagCloud.FileReader; -using TagCloud.Settings; -using TagCloud.WordFilter; +using TagCloud.FileSaver; +using TagCloud.Filter; using TagCloud.WordRanker; using TagCloud.WordsPreprocessor; @@ -11,13 +11,14 @@ namespace TagCloud.UserInterface; public class ConsoleUI : IUserInterface { private readonly IFileReader reader; - private readonly ICloudSaver saver; + private readonly ISaver saver; private readonly IDrawer drawer; private readonly IWordRanker ranker; private readonly IFilter filter; private readonly IPreprocessor preprocessor; - public ConsoleUI(IFileReader reader, ICloudSaver saver, IDrawer drawer, IWordRanker ranker, IFilter filter, IPreprocessor preprocessor) + public ConsoleUI(IFileReader reader, ISaver saver, IDrawer drawer, IWordRanker ranker, IFilter filter, + IPreprocessor preprocessor) { this.reader = reader; this.saver = saver; diff --git a/TagCloud/UserInterface/IUserInterface.cs b/TagCloud/UserInterface/IUserInterface.cs index a7cf4314c..2372db3c6 100644 --- a/TagCloud/UserInterface/IUserInterface.cs +++ b/TagCloud/UserInterface/IUserInterface.cs @@ -1,4 +1,4 @@ -using TagCloud.Settings; +using TagCloud.AppSettings; namespace TagCloud.UserInterface; diff --git a/TagCloud/result.png b/TagCloud/result.png index 063517bfacc1702cc03c169bb0c95ae38345a46d..0f480c0803d2e86aa9ae15f1abd9c145803d268c 100644 GIT binary patch literal 46512 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

=*=#NV=! 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 M4g~D4-iN*Xzgk9f2LJ#7 literal 37179 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 B&@uo3 diff --git a/TagCloud/text.txt b/TagCloud/text.txt index d3cf4c5ac..4f5c9e6db 100644 --- a/TagCloud/text.txt +++ b/TagCloud/text.txt @@ -20,6 +20,12 @@ aliquet vel dapibus id +consectetuer +adipiscing +elit +Phasellus +hendrerit +Pellentesque mattis vel nisi @@ -36,6 +42,12 @@ leo eget blandit nunc +consectetuer +adipiscing +elit +Phasellus +hendrerit +Pellentesque tortor eu nibh @@ -64,6 +76,12 @@ augue Morbi purus libero +consectetuer +adipiscing +elit +Phasellus +hendrerit +Pellentesque faucibus adipiscing commodo @@ -85,6 +103,12 @@ felis Vestibulum volutpat lacus +consectetuer +adipiscing +elit +Phasellus +hendrerit +Pellentesque a ultrices sagittis @@ -108,6 +132,10 @@ et dapibus sed urna +hendrerit +tortor +Sed +semper Morbi interdum mollis @@ -127,6 +155,12 @@ pulvinar risus vitae facilisis +Nullam +quis +massa +sit +amet +nibh libero dolor a @@ -137,11 +171,20 @@ lacus Mauris nibh felis +hendrerit +tortor +Sed +semper adipiscing varius adipiscing in lacinia +risus +vitae +facilisis +libero +dolor vel tellus Suspendisse @@ -161,6 +204,17 @@ gravida vitae ultricies ac +risus +Nullam +quis +massa +sit +amet +nibh +vitae +facilisis +libero +dolor leo Integer leo @@ -178,6 +232,11 @@ Fusce accumsan mollis eros +risus +vitae +facilisis +libero +dolor Pellentesque a diam @@ -207,6 +266,12 @@ quis faucibus non congue +Nullam +quis +massa +sit +amet +nibh vel arcu Ut diff --git a/TagCloudTests/ConsoleUI_Should.cs b/TagCloudTests/ConsoleUI_Should.cs index 2ec83dc91..7edd46d6d 100644 --- a/TagCloudTests/ConsoleUI_Should.cs +++ b/TagCloudTests/ConsoleUI_Should.cs @@ -1,13 +1,13 @@ using Autofac; using CommandLine; -using TagCloud.CloudDrawer; -using TagCloud.CloudSaver; +using TagCloud.AppSettings; +using TagCloud.Drawer; using TagCloud.FileReader; +using TagCloud.FileSaver; +using TagCloud.Filter; using TagCloud.Layouter; using TagCloud.PointGenerator; -using TagCloud.Settings; using TagCloud.UserInterface; -using TagCloud.WordFilter; using TagCloud.WordRanker; using TagCloud.WordsPreprocessor; @@ -26,7 +26,7 @@ public void OneTimeSetUp() var builder = new ContainerBuilder(); builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); @@ -36,7 +36,7 @@ public void OneTimeSetUp() builder.RegisterType().As(); builder.Register(l => - new Layouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), + new CircularLayouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), settings.CloudDensity))).As(); builder.Register(s => settings).AsImplementedInterfaces(); diff --git a/TagCloudTests/IOTests/CloudSaver_Should.cs b/TagCloudTests/IOTests/CloudSaver_Should.cs index a1fac29de..1bb40a714 100644 --- a/TagCloudTests/IOTests/CloudSaver_Should.cs +++ b/TagCloudTests/IOTests/CloudSaver_Should.cs @@ -1,25 +1,25 @@ using NUnit.Framework.Internal; -using TagCloud.CloudSaver; +using TagCloud.FileSaver; namespace TagCloudTests; [TestFixture] public class CloudSaver_Should { - private ICloudSaver sut = new CloudSaver(); + private ISaver sut = new ImageSaver(); private const string filename = "test"; private const string extension = "png"; - + [Test] public void SaveBitmapToFile() { using var bitmap = new Bitmap(50, 50); - + sut.Save(bitmap, filename, extension); File.Exists($"{filename}.{extension}").Should().BeTrue(); - + File.Delete($"{filename}.{extension}"); } } \ No newline at end of file diff --git a/TagCloudTests/IOTests/FileReader_Should.cs b/TagCloudTests/IOTests/FileReader_Should.cs index 8fdcc947d..f60156eff 100644 --- a/TagCloudTests/IOTests/FileReader_Should.cs +++ b/TagCloudTests/IOTests/FileReader_Should.cs @@ -6,7 +6,7 @@ namespace TagCloudTests; [TestFixture] public class FileReader_Should { - private IFileReader sut = new FileReader(); + private IFileReader sut = new TxtReader(); private const string inputPath = "test.txt"; private string text = $"one{Environment.NewLine}two{Environment.NewLine}three{Environment.NewLine}"; @@ -15,7 +15,7 @@ public void SetUp() { using var fileStream = File.Open(inputPath, FileMode.Create); using var writer = new StreamWriter(fileStream); - + writer.Write(text); } @@ -23,11 +23,11 @@ public void SetUp() public void ReadWordsFromTxt() { var expected = text.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).ToList(); - + var result = sut.ReadLines(inputPath); result.Should().BeEquivalentTo(expected); - + File.Delete(inputPath); } } \ No newline at end of file diff --git a/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs b/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs index c89600a6e..8eb4ff188 100644 --- a/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs +++ b/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs @@ -1,5 +1,4 @@ using NUnit.Framework.Interfaces; -using TagCloud.CloudDrawer; using TagCloud.Layouter; using TagCloud.PointGenerator; @@ -8,13 +7,13 @@ public class CircularCloudLayouter_Should { private const int Width = 1920; private const int Height = 1080; - private Layouter sut; + private CircularLayouter sut; [SetUp] public void Setup() { - sut = new Layouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); + sut = new CircularLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); } [Test] diff --git a/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs b/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs index 53ddeeee7..b46d6a7a8 100644 --- a/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs +++ b/TagCloudTests/WordProcessorsTests/DefaultPreprocessor_Should.cs @@ -6,7 +6,7 @@ namespace TagCloudTests.WordProcessorsTests; public class DefaultPreprocessor_Should { private IPreprocessor sut = new DefaultPreprocessor(); - private List words = new(){"Aba", "caBC", "teSTword", "ZAZ", "word"}; + private List words = new() { "Aba", "caBC", "teSTword", "ZAZ", "word" }; [SetUp] public void SetUp() diff --git a/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs b/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs index dd5d92fbc..660385913 100644 --- a/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs +++ b/TagCloudTests/WordProcessorsTests/WordFilter_Should.cs @@ -1,4 +1,4 @@ -using TagCloud.WordFilter; +using TagCloud.Filter; namespace TagCloudTests.WordProcessorsTests; @@ -6,14 +6,14 @@ namespace TagCloudTests.WordProcessorsTests; public class WordFilter_Should { private IFilter sut; - private List words = new(){"aba", "cabc", "testword", "zaz"}; + private List words = new() { "aba", "cabc", "testword", "zaz" }; [SetUp] public void SetUp() { sut = new WordFilter(); } - + [Test] public void FilterWordList_ByGivenFilter() { @@ -21,7 +21,7 @@ public void FilterWordList_ByGivenFilter() var result = sut.FilterWords(words); - result.Should().Contain(new List {"cabc", "testword"}); + result.Should().Contain(new List { "cabc", "testword" }); } [Test] From 7b38d1c1e5224261c40b4454d14235f9d7065819 Mon Sep 17 00:00:00 2001 From: HardreaM Date: Wed, 24 Jan 2024 00:06:33 +0500 Subject: [PATCH 5/6] Add FileReaderProvider.cs --- TagCloud/FileReader/FileReaderProvider.cs | 33 ++++++++++++++++++++++ TagCloud/FileReader/IFileReader.cs | 2 ++ TagCloud/FileReader/IFileReaderProvider.cs | 6 ++++ TagCloud/FileReader/TxtReader.cs | 9 ++++++ TagCloud/Program.cs | 1 + TagCloud/UserInterface/ConsoleUI.cs | 9 +++--- TagCloudTests/ConsoleUI_Should.cs | 6 ++++ 7 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 TagCloud/FileReader/FileReaderProvider.cs create mode 100644 TagCloud/FileReader/IFileReaderProvider.cs diff --git a/TagCloud/FileReader/FileReaderProvider.cs b/TagCloud/FileReader/FileReaderProvider.cs new file mode 100644 index 000000000..90c515a2b --- /dev/null +++ b/TagCloud/FileReader/FileReaderProvider.cs @@ -0,0 +1,33 @@ +namespace TagCloud.FileReader; + +public class FileReaderProvider : IFileReaderProvider +{ + private Dictionary readers; + + public FileReaderProvider(IEnumerable readers) + { + this.readers = ArrangeReaders(readers); + } + + public IFileReader CreateReader(string inputPath) + { + var extension = inputPath.Split(".").Last(); + if (readers.ContainsKey(extension)) + return readers[extension]; + throw new ArgumentException($"{extension} file type is not supported"); + } + + private Dictionary ArrangeReaders(IEnumerable readers) + { + var readersDictionary = new Dictionary(); + foreach (var reader in readers) + { + foreach (var extension in reader.GetAviableExtensions()) + { + readersDictionary[extension] = reader; + } + } + + return readersDictionary; + } +} \ No newline at end of file diff --git a/TagCloud/FileReader/IFileReader.cs b/TagCloud/FileReader/IFileReader.cs index 1c3ba806a..39f253c43 100644 --- a/TagCloud/FileReader/IFileReader.cs +++ b/TagCloud/FileReader/IFileReader.cs @@ -3,4 +3,6 @@ namespace TagCloud.FileReader; public interface IFileReader { IEnumerable ReadLines(string inputPath); + + IList GetAviableExtensions(); } \ No newline at end of file diff --git a/TagCloud/FileReader/IFileReaderProvider.cs b/TagCloud/FileReader/IFileReaderProvider.cs new file mode 100644 index 000000000..37c156359 --- /dev/null +++ b/TagCloud/FileReader/IFileReaderProvider.cs @@ -0,0 +1,6 @@ +namespace TagCloud.FileReader; + +public interface IFileReaderProvider +{ + IFileReader CreateReader(string inputPath); +} \ No newline at end of file diff --git a/TagCloud/FileReader/TxtReader.cs b/TagCloud/FileReader/TxtReader.cs index 63395bf0f..a7b092527 100644 --- a/TagCloud/FileReader/TxtReader.cs +++ b/TagCloud/FileReader/TxtReader.cs @@ -1,7 +1,11 @@ +using System.Xml.XPath; + namespace TagCloud.FileReader; public class TxtReader : IFileReader { + private List extensions = new() { "txt" }; + public IEnumerable ReadLines(string InputPath) { if (!File.Exists(InputPath)) @@ -9,4 +13,9 @@ public IEnumerable ReadLines(string InputPath) return File.ReadLines(InputPath); } + + public IList GetAviableExtensions() + { + return extensions; + } } \ No newline at end of file diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 84549432f..79e72c58b 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -30,6 +30,7 @@ static void Main(string[] args) builder.Register(c => new CircularLayouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), settings.CloudDensity))).As(); + builder.Register(c => new FileReaderProvider(c.Resolve>())).As(); builder.Register(c => settings).AsImplementedInterfaces(); diff --git a/TagCloud/UserInterface/ConsoleUI.cs b/TagCloud/UserInterface/ConsoleUI.cs index f3f927441..c1cbdf9ea 100644 --- a/TagCloud/UserInterface/ConsoleUI.cs +++ b/TagCloud/UserInterface/ConsoleUI.cs @@ -10,17 +10,18 @@ namespace TagCloud.UserInterface; public class ConsoleUI : IUserInterface { - private readonly IFileReader reader; + private readonly IFileReaderProvider readerProvider; private readonly ISaver saver; private readonly IDrawer drawer; private readonly IWordRanker ranker; private readonly IFilter filter; private readonly IPreprocessor preprocessor; - public ConsoleUI(IFileReader reader, ISaver saver, IDrawer drawer, IWordRanker ranker, IFilter filter, + public ConsoleUI(IFileReaderProvider readerProvider, ISaver saver, IDrawer drawer, IWordRanker ranker, + IFilter filter, IPreprocessor preprocessor) { - this.reader = reader; + this.readerProvider = readerProvider; this.saver = saver; this.drawer = drawer; this.ranker = ranker; @@ -30,7 +31,7 @@ public ConsoleUI(IFileReader reader, ISaver saver, IDrawer drawer, IWordRanker r public void Run(IAppSettings appSettings) { - var words = reader.ReadLines(appSettings.InputPath); + var words = readerProvider.CreateReader($"{appSettings.InputPath}").ReadLines(appSettings.InputPath); var preprocessed = preprocessor.HandleWords(words); var filtered = filter.FilterWords(preprocessed); var ranked = ranker.RankWords(filtered); diff --git a/TagCloudTests/ConsoleUI_Should.cs b/TagCloudTests/ConsoleUI_Should.cs index 7edd46d6d..983e19ff3 100644 --- a/TagCloudTests/ConsoleUI_Should.cs +++ b/TagCloudTests/ConsoleUI_Should.cs @@ -38,6 +38,7 @@ public void OneTimeSetUp() builder.Register(l => new CircularLayouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), settings.CloudDensity))).As(); + builder.Register(c => new FileReaderProvider(c.Resolve>())).As(); builder.Register(s => settings).AsImplementedInterfaces(); @@ -61,5 +62,10 @@ public IEnumerable ReadLines(string inputPath) { yield return "test"; } + + public IList GetAviableExtensions() + { + return new List() {"txt"}; + } } } \ No newline at end of file From 7360db29f5f4552e46933c14bba7f489e0bec70a Mon Sep 17 00:00:00 2001 From: HardreaM Date: Wed, 24 Jan 2024 22:52:53 +0500 Subject: [PATCH 6/6] Refactoring; Add Doc/Docx Reader; Add CircularPointerGenerator --- TagCloud/AppSettings/Settings.cs | 10 ++--- TagCloud/Configurator.cs | 31 ++++++++++++++++ TagCloud/Drawer/CloudDrawer.cs | 5 ++- TagCloud/FileReader/DocReader.cs | 19 ++++++++++ TagCloud/FileReader/FileReaderProvider.cs | 4 +- TagCloud/FileReader/IFileReader.cs | 2 +- TagCloud/FileReader/TxtReader.cs | 10 ++--- TagCloud/FileSaver/ISaver.cs | 2 +- .../{CircularLayouter.cs => CloudLayouter.cs} | 4 +- TagCloud/PointGenerator/CirclesGenerator.cs | 35 ++++++++++++++++++ TagCloud/PointGenerator/IPointGenerator.cs | 1 + .../PointGenerator/IPointGeneratorProvider.cs | 6 +++ .../PointGenerator/PointGeneratorProvider.cs | 29 +++++++++++++++ TagCloud/PointGenerator/SpiralGenerator.cs | 2 + TagCloud/Program.cs | 26 +------------ TagCloud/TagCloud.csproj | 1 + TagCloud/result.png | Bin 46512 -> 37358 bytes TagCloudTests/ConsoleUI_Should.cs | 25 ++----------- .../CircularCloudLayouter_Should.cs | 8 ++-- 19 files changed, 152 insertions(+), 68 deletions(-) create mode 100644 TagCloud/FileReader/DocReader.cs rename TagCloud/Layouter/{CircularLayouter.cs => CloudLayouter.cs} (90%) create mode 100644 TagCloud/PointGenerator/CirclesGenerator.cs create mode 100644 TagCloud/PointGenerator/IPointGeneratorProvider.cs create mode 100644 TagCloud/PointGenerator/PointGeneratorProvider.cs diff --git a/TagCloud/AppSettings/Settings.cs b/TagCloud/AppSettings/Settings.cs index 5ef797d47..5ddc38455 100644 --- a/TagCloud/AppSettings/Settings.cs +++ b/TagCloud/AppSettings/Settings.cs @@ -22,21 +22,21 @@ public class Settings : IAppSettings [Option('H', "height", Default = 1080, HelpText = "Height of cloud")] public int CloudHeight { get; set; } - [Option('l', "layouter", Default = "Circular", HelpText = "Cloud layouter algorithm")] + [Option('l', "layouter", Default = "Spiral", HelpText = "Cloud layouter algorithm")] public string LayouterType { get; set; } [Option('d', "density", Default = 1, HelpText = "Density of cloud")] public int CloudDensity { get; set; } - + [Option('r', "randomPalette", Default = true, HelpText = "Use random colors")] public bool UseRandomPalette { get; set; } - + [Option("background", Default = "White", HelpText = "Cloud layouter algorithm")] public string BackgroundColor { get; set; } - + [Option("foreground", Default = "Black", HelpText = "Cloud layouter algorithm")] public string ForegroundColor { get; set; } - + [Option("boringWordsFile", Default = null, HelpText = "Cloud layouter algorithm")] public string BoringWordsFile { get; set; } } \ No newline at end of file diff --git a/TagCloud/Configurator.cs b/TagCloud/Configurator.cs index 6dc88aad1..022973dde 100644 --- a/TagCloud/Configurator.cs +++ b/TagCloud/Configurator.cs @@ -4,7 +4,12 @@ using TagCloud.AppSettings; using TagCloud.Drawer; using TagCloud.FileReader; +using TagCloud.FileSaver; using TagCloud.Filter; +using TagCloud.PointGenerator; +using TagCloud.UserInterface; +using TagCloud.WordRanker; +using TagCloud.WordsPreprocessor; namespace TagCloud; @@ -31,4 +36,30 @@ public static IAppSettings Parse(string[] args, ContainerBuilder builder) return settings.Value; } + + public static ContainerBuilder BuildWithSettings(IAppSettings settings, ContainerBuilder builder) + { + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + + builder.Register(c => new WordFilter().UsingFilter((word) => word.Length > 3)).As(); + builder.Register(c => + new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), settings.CloudDensity)) + .As(); + builder.Register(c => new CirclesGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2))) + .As(); + builder.Register(c => new FileReaderProvider(c.Resolve>())).As(); + builder.Register(c => new PointGeneratorProvider(c.Resolve>())) + .As(); + + builder.Register(c => settings).AsImplementedInterfaces(); + + return builder; + } } \ No newline at end of file diff --git a/TagCloud/Drawer/CloudDrawer.cs b/TagCloud/Drawer/CloudDrawer.cs index 976283b84..10ca9ad19 100644 --- a/TagCloud/Drawer/CloudDrawer.cs +++ b/TagCloud/Drawer/CloudDrawer.cs @@ -1,6 +1,7 @@ using System.Drawing; using TagCloud.AppSettings; using TagCloud.Layouter; +using TagCloud.PointGenerator; namespace TagCloud.Drawer; @@ -14,9 +15,9 @@ public class CloudDrawer : IDrawer private const int MaximalFontSize = 50; private const int LengthSizeMultiplier = 35; - public CloudDrawer(ILayouter layouter, IPalette palette, IAppSettings appSettings) + public CloudDrawer(IPointGeneratorProvider pointGenerator, IPalette palette, IAppSettings appSettings) { - this.layouter = layouter; + layouter = new CloudLayouter(pointGenerator.CreateGenerator(appSettings.LayouterType)); this.palette = palette; this.appSettings = appSettings; } diff --git a/TagCloud/FileReader/DocReader.cs b/TagCloud/FileReader/DocReader.cs new file mode 100644 index 000000000..b19c19815 --- /dev/null +++ b/TagCloud/FileReader/DocReader.cs @@ -0,0 +1,19 @@ +using Spire.Doc; + +namespace TagCloud.FileReader; + +public class DocReader : IFileReader +{ + public IEnumerable ReadLines(string inputPath) + { + if (!File.Exists(inputPath)) + throw new ArgumentException("Source file doesn't exist"); + + var document = new Document(inputPath, FileFormat.Auto); + var text = document.GetText(); + + return text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Skip(1); + } + + public IList GetAvailableExtensions() => new List() { "doc", "docx" }; +} \ No newline at end of file diff --git a/TagCloud/FileReader/FileReaderProvider.cs b/TagCloud/FileReader/FileReaderProvider.cs index 90c515a2b..20bb8ab84 100644 --- a/TagCloud/FileReader/FileReaderProvider.cs +++ b/TagCloud/FileReader/FileReaderProvider.cs @@ -8,7 +8,7 @@ public FileReaderProvider(IEnumerable readers) { this.readers = ArrangeReaders(readers); } - + public IFileReader CreateReader(string inputPath) { var extension = inputPath.Split(".").Last(); @@ -22,7 +22,7 @@ private Dictionary ArrangeReaders(IEnumerable var readersDictionary = new Dictionary(); foreach (var reader in readers) { - foreach (var extension in reader.GetAviableExtensions()) + foreach (var extension in reader.GetAvailableExtensions()) { readersDictionary[extension] = reader; } diff --git a/TagCloud/FileReader/IFileReader.cs b/TagCloud/FileReader/IFileReader.cs index 39f253c43..6c32bfc8a 100644 --- a/TagCloud/FileReader/IFileReader.cs +++ b/TagCloud/FileReader/IFileReader.cs @@ -4,5 +4,5 @@ public interface IFileReader { IEnumerable ReadLines(string inputPath); - IList GetAviableExtensions(); + IList GetAvailableExtensions(); } \ No newline at end of file diff --git a/TagCloud/FileReader/TxtReader.cs b/TagCloud/FileReader/TxtReader.cs index a7b092527..8cd3de0cd 100644 --- a/TagCloud/FileReader/TxtReader.cs +++ b/TagCloud/FileReader/TxtReader.cs @@ -5,16 +5,16 @@ namespace TagCloud.FileReader; public class TxtReader : IFileReader { private List extensions = new() { "txt" }; - - public IEnumerable ReadLines(string InputPath) + + public IEnumerable ReadLines(string inputPath) { - if (!File.Exists(InputPath)) + if (!File.Exists(inputPath)) throw new ArgumentException("Source file doesn't exist"); - return File.ReadLines(InputPath); + return File.ReadLines(inputPath); } - public IList GetAviableExtensions() + public IList GetAvailableExtensions() { return extensions; } diff --git a/TagCloud/FileSaver/ISaver.cs b/TagCloud/FileSaver/ISaver.cs index 34344ad06..58b6f035e 100644 --- a/TagCloud/FileSaver/ISaver.cs +++ b/TagCloud/FileSaver/ISaver.cs @@ -4,5 +4,5 @@ namespace TagCloud.FileSaver; public interface ISaver { - void Save(Bitmap bitmap, string OutputPath, string imageFormat); + void Save(Bitmap bitmap, string outputPath, string imageFormat); } \ No newline at end of file diff --git a/TagCloud/Layouter/CircularLayouter.cs b/TagCloud/Layouter/CloudLayouter.cs similarity index 90% rename from TagCloud/Layouter/CircularLayouter.cs rename to TagCloud/Layouter/CloudLayouter.cs index 9a5e0a6d3..d5866c938 100644 --- a/TagCloud/Layouter/CircularLayouter.cs +++ b/TagCloud/Layouter/CloudLayouter.cs @@ -3,12 +3,12 @@ namespace TagCloud.Layouter; -public class CircularLayouter : ILayouter +public class CloudLayouter : ILayouter { private readonly IPointGenerator pointGenerator; public IList Rectangles { get; } - public CircularLayouter(IPointGenerator pointGenerator) + public CloudLayouter(IPointGenerator pointGenerator) { Rectangles = new List(); this.pointGenerator = pointGenerator; diff --git a/TagCloud/PointGenerator/CirclesGenerator.cs b/TagCloud/PointGenerator/CirclesGenerator.cs new file mode 100644 index 000000000..80842693f --- /dev/null +++ b/TagCloud/PointGenerator/CirclesGenerator.cs @@ -0,0 +1,35 @@ +using System.Drawing; + +namespace TagCloud.PointGenerator; + +public class CirclesGenerator : IPointGenerator +{ + private readonly Point startPoint; + private int density; + private readonly double angleShift; + private double currentAngle; + + public string GeneratorName => "Circular"; + + public CirclesGenerator(Point startPoint, int density = 1, double angleShift = 0.01) + { + if (startPoint.X < 0 || startPoint.Y < 0) + throw new ArgumentException("Circle center point coordinates should be non-negative"); + this.startPoint = startPoint; + this.density = density * 200; + this.angleShift = angleShift; + } + + public Point GetNextPoint() + { + var radius = density; + var x = (int)(Math.Cos(currentAngle) * radius); + var y = (int)(Math.Sin(currentAngle) * radius); + currentAngle += angleShift; + + if (currentAngle < 2 * Math.PI && currentAngle + angleShift > 2 * Math.PI) + density += 100; + + return new Point(startPoint.X + x, startPoint.Y + y); + } +} \ No newline at end of file diff --git a/TagCloud/PointGenerator/IPointGenerator.cs b/TagCloud/PointGenerator/IPointGenerator.cs index 8388b83d7..6bc7323d0 100644 --- a/TagCloud/PointGenerator/IPointGenerator.cs +++ b/TagCloud/PointGenerator/IPointGenerator.cs @@ -4,5 +4,6 @@ namespace TagCloud.PointGenerator; public interface IPointGenerator { + string GeneratorName { get; } Point GetNextPoint(); } \ No newline at end of file diff --git a/TagCloud/PointGenerator/IPointGeneratorProvider.cs b/TagCloud/PointGenerator/IPointGeneratorProvider.cs new file mode 100644 index 000000000..d32163863 --- /dev/null +++ b/TagCloud/PointGenerator/IPointGeneratorProvider.cs @@ -0,0 +1,6 @@ +namespace TagCloud.PointGenerator; + +public interface IPointGeneratorProvider +{ + IPointGenerator CreateGenerator(string generatorName); +} \ No newline at end of file diff --git a/TagCloud/PointGenerator/PointGeneratorProvider.cs b/TagCloud/PointGenerator/PointGeneratorProvider.cs new file mode 100644 index 000000000..7bcd6206f --- /dev/null +++ b/TagCloud/PointGenerator/PointGeneratorProvider.cs @@ -0,0 +1,29 @@ +namespace TagCloud.PointGenerator; + +public class PointGeneratorProvider : IPointGeneratorProvider +{ + private Dictionary registeredGenerators; + + public PointGeneratorProvider(IEnumerable generators) + { + registeredGenerators = ArrangeLayouters(generators); + } + + public IPointGenerator CreateGenerator(string generatorName) + { + if (registeredGenerators.ContainsKey(generatorName)) + return registeredGenerators[generatorName]; + throw new ArgumentException($"{generatorName} layouter is not supported"); + } + + private Dictionary ArrangeLayouters(IEnumerable generators) + { + var generatorsDictionary = new Dictionary(); + foreach (var generator in generators) + { + generatorsDictionary[generator.GeneratorName] = generator; + } + + return generatorsDictionary; + } +} \ No newline at end of file diff --git a/TagCloud/PointGenerator/SpiralGenerator.cs b/TagCloud/PointGenerator/SpiralGenerator.cs index 5eb1c50c6..cbf60246f 100644 --- a/TagCloud/PointGenerator/SpiralGenerator.cs +++ b/TagCloud/PointGenerator/SpiralGenerator.cs @@ -9,6 +9,8 @@ public class SpiralGenerator : IPointGenerator private readonly double angleShift; private double currentAngle; + public string GeneratorName => "Spiral"; + public SpiralGenerator(Point startPoint, int spiralDensity = 1, double angleShift = 0.01) { if (startPoint.X < 0 || startPoint.Y < 0) diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 79e72c58b..044a854be 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -1,14 +1,5 @@ -using System.Drawing; -using Autofac; -using TagCloud.Drawer; -using TagCloud.FileReader; -using TagCloud.FileSaver; -using TagCloud.Filter; -using TagCloud.Layouter; -using TagCloud.PointGenerator; +using Autofac; using TagCloud.UserInterface; -using TagCloud.WordRanker; -using TagCloud.WordsPreprocessor; namespace TagCloud; @@ -18,21 +9,8 @@ static void Main(string[] args) { var builder = new ContainerBuilder(); var settings = Configurator.Parse(args, builder); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - - builder.Register(c => new WordFilter().UsingFilter((word) => word.Length > 3)).As(); - builder.Register(c => - new CircularLayouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), - settings.CloudDensity))).As(); - builder.Register(c => new FileReaderProvider(c.Resolve>())).As(); - - builder.Register(c => settings).AsImplementedInterfaces(); + builder = Configurator.BuildWithSettings(settings, builder); var container = builder.Build(); container.Resolve().Run(settings); diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj index 95be83ce2..e109de6ce 100644 --- a/TagCloud/TagCloud.csproj +++ b/TagCloud/TagCloud.csproj @@ -10,6 +10,7 @@ + diff --git a/TagCloud/result.png b/TagCloud/result.png index 0f480c0803d2e86aa9ae15f1abd9c145803d268c..6a5f676a7b4526cee89017ff37de55cf28809c1b 100644 GIT binary patch literal 37358 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 x7>ox7W35wRNji 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

=*=#NV=! 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 M4g~D4-iN*Xzgk9f2LJ#7 diff --git a/TagCloudTests/ConsoleUI_Should.cs b/TagCloudTests/ConsoleUI_Should.cs index 983e19ff3..57a184203 100644 --- a/TagCloudTests/ConsoleUI_Should.cs +++ b/TagCloudTests/ConsoleUI_Should.cs @@ -3,13 +3,8 @@ using TagCloud.AppSettings; using TagCloud.Drawer; using TagCloud.FileReader; -using TagCloud.FileSaver; using TagCloud.Filter; -using TagCloud.Layouter; -using TagCloud.PointGenerator; using TagCloud.UserInterface; -using TagCloud.WordRanker; -using TagCloud.WordsPreprocessor; namespace TagCloudTests; @@ -25,22 +20,10 @@ public void OneTimeSetUp() settings = Parser.Default.ParseArguments(new List()).Value; var builder = new ContainerBuilder(); + builder = Configurator.BuildWithSettings(settings, builder); builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.Register(l => - new CircularLayouter(new SpiralGenerator(new Point(settings.CloudWidth / 2, settings.CloudWidth / 2), - settings.CloudDensity))).As(); - builder.Register(c => new FileReaderProvider(c.Resolve>())).As(); - - builder.Register(s => settings).AsImplementedInterfaces(); + builder.RegisterType().As(); var container = builder.Build(); sut = container.Resolve(); @@ -63,9 +46,9 @@ public IEnumerable ReadLines(string inputPath) yield return "test"; } - public IList GetAviableExtensions() + public IList GetAvailableExtensions() { - return new List() {"txt"}; + return new List() { "txt" }; } } } \ No newline at end of file diff --git a/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs b/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs index 8eb4ff188..0fca20ee1 100644 --- a/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs +++ b/TagCloudTests/LayouterTests/CircularCloudLayouter_Should.cs @@ -1,4 +1,3 @@ -using NUnit.Framework.Interfaces; using TagCloud.Layouter; using TagCloud.PointGenerator; @@ -7,13 +6,12 @@ public class CircularCloudLayouter_Should { private const int Width = 1920; private const int Height = 1080; - private CircularLayouter sut; - - + private CloudLayouter sut; + [SetUp] public void Setup() { - sut = new CircularLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); + sut = new CloudLayouter(new SpiralGenerator(new Point(Width / 2, Height / 2))); } [Test]