From 54381c5414be561e0a9e3dfb0fefeae61d46696e Mon Sep 17 00:00:00 2001 From: PavelUd Date: Tue, 16 Jan 2024 10:44:26 +0500 Subject: [PATCH 01/10] add TagsCloud --- .gitignore | 1 + TagsCloud/CircularCloudLayouter.cs | 46 ++++++++++ TagsCloud/CloudLayouterExtension.cs | 11 +++ TagsCloud/Folder.DotSettings | 2 + TagsCloud/ICircularCloudLayouter.cs | 9 ++ TagsCloud/ISpiral.cs | 8 ++ TagsCloud/MainProgram.cs | 34 ++++++++ TagsCloud/README.md | 4 + TagsCloud/RectanglesVisualizer.cs | 62 ++++++++++++++ TagsCloud/Spiral.cs | 28 ++++++ TagsCloud/TagsCloud.csproj | 15 ++++ TagsCloud/Utils.cs | 20 +++++ TagsCloudTests/CircularCloudLayouterTests.cs | 89 ++++++++++++++++++++ TagsCloudTests/GlobalUsings.cs | 1 + TagsCloudTests/RectanglesVisualizerTests.cs | 53 ++++++++++++ TagsCloudTests/SpiralTests.cs | 46 ++++++++++ TagsCloudTests/TagsCloudTests.csproj | 22 +++++ di.sln | 12 +++ 18 files changed, 463 insertions(+) create mode 100644 TagsCloud/CircularCloudLayouter.cs create mode 100644 TagsCloud/CloudLayouterExtension.cs create mode 100644 TagsCloud/Folder.DotSettings create mode 100644 TagsCloud/ICircularCloudLayouter.cs create mode 100644 TagsCloud/ISpiral.cs create mode 100644 TagsCloud/MainProgram.cs create mode 100644 TagsCloud/README.md create mode 100644 TagsCloud/RectanglesVisualizer.cs create mode 100644 TagsCloud/Spiral.cs create mode 100644 TagsCloud/TagsCloud.csproj create mode 100644 TagsCloud/Utils.cs create mode 100644 TagsCloudTests/CircularCloudLayouterTests.cs create mode 100644 TagsCloudTests/GlobalUsings.cs create mode 100644 TagsCloudTests/RectanglesVisualizerTests.cs create mode 100644 TagsCloudTests/SpiralTests.cs create mode 100644 TagsCloudTests/TagsCloudTests.csproj diff --git a/.gitignore b/.gitignore index e65efedf2..ba903011c 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ artifacts/ *.pidb *.svclog *.scc +*.png # Chutzpah Test files _Chutzpah* diff --git a/TagsCloud/CircularCloudLayouter.cs b/TagsCloud/CircularCloudLayouter.cs new file mode 100644 index 000000000..b0dd0a203 --- /dev/null +++ b/TagsCloud/CircularCloudLayouter.cs @@ -0,0 +1,46 @@ +using System.Drawing; + +namespace TagsCloud; + +public class CircularCloudLayouter : ICircularCloudLayouter +{ + public List Rectangles { get; } + + private readonly ISpiral spiral; + + public CircularCloudLayouter(ISpiral spiral) + { + this.spiral = spiral; + Rectangles = new List(); + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) + throw new ArgumentException("Sides of the rectangle should not be non-positive"); + var rectangles = CreateNextRectangle(rectangleSize); + Rectangles.Add(rectangles); + return rectangles; + } + + private Rectangle CreateNextRectangle(Size rectangleSize) + { + var point = spiral.GetPoint(); + var rectangles = new Rectangle(point, rectangleSize); + while (!HasNoIntersections(rectangles)) + { + point = spiral.GetPoint(); + rectangles = new Rectangle(point, rectangleSize); + } + + return rectangles; + } + + private bool HasNoIntersections(Rectangle rectangles) + { + for (var i = Rectangles.Count - 1; i >= 0; i--) + if (Rectangles[i].IntersectsWith(rectangles)) + return false; + return true; + } +} \ No newline at end of file diff --git a/TagsCloud/CloudLayouterExtension.cs b/TagsCloud/CloudLayouterExtension.cs new file mode 100644 index 000000000..9a2361902 --- /dev/null +++ b/TagsCloud/CloudLayouterExtension.cs @@ -0,0 +1,11 @@ +using System.Drawing; + +namespace TagsCloud; + +public static class CloudLayouterExtension +{ + public static List GetRectanglesLocation(this ICircularCloudLayouter layouter) + { + return layouter.Rectangles.Select(rectangle => rectangle.Location).ToList(); + } +} \ No newline at end of file diff --git a/TagsCloud/Folder.DotSettings b/TagsCloud/Folder.DotSettings new file mode 100644 index 000000000..06b71d5de --- /dev/null +++ b/TagsCloud/Folder.DotSettings @@ -0,0 +1,2 @@ + + False \ No newline at end of file diff --git a/TagsCloud/ICircularCloudLayouter.cs b/TagsCloud/ICircularCloudLayouter.cs new file mode 100644 index 000000000..103619a6b --- /dev/null +++ b/TagsCloud/ICircularCloudLayouter.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagsCloud; + +public interface ICircularCloudLayouter +{ + List Rectangles { get; } + Rectangle PutNextRectangle(Size rectangleSize); +} \ No newline at end of file diff --git a/TagsCloud/ISpiral.cs b/TagsCloud/ISpiral.cs new file mode 100644 index 000000000..cb2d4cb80 --- /dev/null +++ b/TagsCloud/ISpiral.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloud; + +public interface ISpiral +{ + Point GetPoint(); +} \ No newline at end of file diff --git a/TagsCloud/MainProgram.cs b/TagsCloud/MainProgram.cs new file mode 100644 index 000000000..a19762d5b --- /dev/null +++ b/TagsCloud/MainProgram.cs @@ -0,0 +1,34 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Imaging; + +namespace TagsCloud; + +public class MainProgram +{ + public static void Main(string[] args) + { + var spiral = new Spiral(new Point(100, 100)); + var layout = new CircularCloudLayouter(spiral); + + for (var i = 0; i < 10000; i++) + { + var rectangle = layout.PutNextRectangle(Utils.GetRandomSize()); + } + + var workingDirectory = Environment.CurrentDirectory; + var imagesDirectoryPath = Path.Combine(workingDirectory, "images"); + + if (!Directory.Exists(imagesDirectoryPath)) + { + Directory.CreateDirectory(imagesDirectoryPath); + } + + const string imageName = "1077rect"; + var imagePath = Path.Combine(imagesDirectoryPath, $"{imageName}.png"); + + using var image = RectanglesVisualizer.GetTagsCloudImage(layout.Rectangles); + image.Save(imagePath, ImageFormat.Png); + } +} \ No newline at end of file diff --git a/TagsCloud/README.md b/TagsCloud/README.md new file mode 100644 index 000000000..eda34029e --- /dev/null +++ b/TagsCloud/README.md @@ -0,0 +1,4 @@ +![10000 random rectangles](https://github.com/PavelUd/tdd/blob/master/cs/tagsCloud/images/10000rect.png) +![1000 random rectangles](https://github.com/PavelUd/tdd/blob/master/cs/tagsCloud/images/1000rect.png) +![100 random rectangles](https://github.com/PavelUd/tdd/blob/master/cs/tagsCloud/images/100rect.png) +![10 random rectangles](https://github.com/PavelUd/tdd/blob/master/cs/tagsCloud/images/10rect.png) \ No newline at end of file diff --git a/TagsCloud/RectanglesVisualizer.cs b/TagsCloud/RectanglesVisualizer.cs new file mode 100644 index 000000000..2bc61a85f --- /dev/null +++ b/TagsCloud/RectanglesVisualizer.cs @@ -0,0 +1,62 @@ +using System.Drawing; + +namespace TagsCloud; + +public static class RectanglesVisualizer +{ + private const int Border = 50; + + public static Bitmap GetTagsCloudImage(List rectangles) + { + if (!rectangles.Any()) return new Bitmap(100, 100); + + var extremePoints = GetRectanglesExtremePoints(rectangles); + var sizeImage = GetImageSize(extremePoints); + var shift = GetRectanglesShift(extremePoints); + var image = new Bitmap(sizeImage.Width, sizeImage.Height); + using var graphics = Graphics.FromImage(image); + var background = new SolidBrush(Color.Black); + graphics.FillRectangle(background, new Rectangle(0, 0, image.Width, image.Height)); + DrawTagsCloud(rectangles, graphics, shift); + + return image; + } + + private static (int Left, int Right, int Top, int Bottom) GetRectanglesExtremePoints(List rectangles) + { + var leftmost = rectangles.Min(rectangle => rectangle.Left); + var rightmost = rectangles.Max(rectangle => rectangle.Right); + var topmost = rectangles.Min(rectangle => rectangle.Top); + var bottommost = rectangles.Max(rectangle => rectangle.Bottom); + + return (leftmost, rightmost, topmost, bottommost); + } + + private static Point GetRectanglesShift((int Left, int Right, int Top, int Bottom) extremePoints) + { + var startX = extremePoints.Top >= 0 ? 0 : extremePoints.Top; + var startY = extremePoints.Left >= 0 ? 0 : extremePoints.Left; + + return new Point(Math.Abs(startX) + Border, Math.Abs(startY) + Border); + } + + + private static Size GetImageSize((int Left, int Right, int Top, int Bottom) extremePoints) + { + var height = Math.Abs(extremePoints.Bottom) + Math.Abs(extremePoints.Top) + 2 * Border; + var width = Math.Abs(extremePoints.Right) + Math.Abs(extremePoints.Left) + 2 * Border; + + return new Size(width, height); + } + + private static void DrawTagsCloud(List rectangles, Graphics graphics, Point shift) + { + foreach (var rectangle in rectangles) + { + var renderedRectangle = + new Rectangle(new Point(rectangle.X + shift.X, rectangle.Y + shift.Y), rectangle.Size); + using var pen = new Pen(Utils.GetRandomColor()); + graphics.DrawRectangle(pen, renderedRectangle); + } + } +} \ No newline at end of file diff --git a/TagsCloud/Spiral.cs b/TagsCloud/Spiral.cs new file mode 100644 index 000000000..188cc08e8 --- /dev/null +++ b/TagsCloud/Spiral.cs @@ -0,0 +1,28 @@ +using System.Drawing; + +namespace TagsCloud; + +public class Spiral : ISpiral +{ + private int counter; + private readonly float step; + private readonly Point center; + + public Spiral(Point center, float step = 0.1f) + { + this.center = center; + if (step == 0) + throw new ArgumentException("the step must not be equal to 0"); + this.step = step; + } + + public Point GetPoint() + { + var angle = step * counter; + var xOffset = (float)(step * angle * Math.Cos(angle)); + var yOffset = (float)(step * angle * Math.Sin(angle)); + var point = new Point((int)Math.Round(center.X + xOffset), (int)Math.Round(center.Y + yOffset)); + counter += 1; + return point; + } +} \ No newline at end of file diff --git a/TagsCloud/TagsCloud.csproj b/TagsCloud/TagsCloud.csproj new file mode 100644 index 000000000..06eb4efbb --- /dev/null +++ b/TagsCloud/TagsCloud.csproj @@ -0,0 +1,15 @@ + + + + net6-windows + enable + enable + Exe + TagsCloud + + + + + + + diff --git a/TagsCloud/Utils.cs b/TagsCloud/Utils.cs new file mode 100644 index 000000000..6a17005f2 --- /dev/null +++ b/TagsCloud/Utils.cs @@ -0,0 +1,20 @@ +using System.Drawing; + +namespace TagsCloud; + +public class Utils +{ + private static readonly Random Random = new(); + private const int MinSize = 1; + private const int MaxSize = 50; + + + public static Color GetRandomColor() => Color.FromArgb(GetShade(), GetShade(), GetShade()); + + + private static int GetShade() => Random.Next(0, 256); + + + public static Size GetRandomSize() => + new Size(Random.Next(MinSize, MaxSize), Random.Next(MinSize, MaxSize)); +} \ No newline at end of file diff --git a/TagsCloudTests/CircularCloudLayouterTests.cs b/TagsCloudTests/CircularCloudLayouterTests.cs new file mode 100644 index 000000000..bf48ec326 --- /dev/null +++ b/TagsCloudTests/CircularCloudLayouterTests.cs @@ -0,0 +1,89 @@ +using System.Drawing; +using System.Drawing.Imaging; +using FluentAssertions; +using NUnit.Framework.Interfaces; +using TagsCloud; + +namespace TagsCloudTests; + +public class CircularCloudLayouterTests +{ + private ISpiral spiral; + private ICircularCloudLayouter sut; + + [SetUp] + public void SetUp() + { + spiral = new Spiral(new Point(10, 10)); + sut = new CircularCloudLayouter(spiral); + } + + [TearDown] + public void TearDown() + { + if (TestContext.CurrentContext.Result.Outcome != ResultState.Failure) return; + var workingDirectory = Environment.CurrentDirectory; + var parentDirectory = Directory.GetParent(workingDirectory)?.Parent; + + var directoryPath = parentDirectory != null + ? Path.Combine(parentDirectory.FullName, "FailedTestImages") + : Path.Combine(workingDirectory, "FailedTestImages"); + + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + + var imageName = TestContext.CurrentContext.Test.Name; + var imagePath = Path.Combine(directoryPath, $"{imageName}.png"); + + RectanglesVisualizer.GetTagsCloudImage(sut.Rectangles).Save(imagePath, ImageFormat.Png); + + Console.WriteLine($"Tag cloud visualization saved to file {imagePath}"); + } + + + private static bool IsRectanglesIntersect(List rectangles) => + rectangles.Any(rectangle => rectangles.Any(nextRectangle => + nextRectangle.IntersectsWith(rectangle) && !rectangle.Equals(nextRectangle))); + + + [Test] + public void GetLocationAfterInitialization_ShouldBeEmpty() + { + var location = sut.GetRectanglesLocation(); + location.Should().BeEmpty(); + } + + [TestCase(-1, 10, TestName = "width is negative")] + [TestCase(1, -10, TestName = "height is negative")] + [TestCase(1, 0, TestName = "Zero height, correct width")] + [TestCase(0, 10, TestName = "Zero width, correct height")] + public void PutRectangleWithNegativeParams_ShouldBeThrowException(int width, int height) + { + var size = new Size(width, height); + Action action = () => sut.PutNextRectangle(size); + action.Should().Throw() + .WithMessage("Sides of the rectangle should not be non-positive"); + } + + [Test] + public void PutOneRectangle_IsNotEmpty() + { + var rectangle = sut.PutNextRectangle(new Size(10, 10)); + var location = sut.GetRectanglesLocation(); + location.Should().NotBeEmpty(); + } + + [Test] + public void Put1000Rectangles_RectanglesShouldNotIntersect() + { + for (var i = 0; i < 1000; i++) + { + var size = Utils.GetRandomSize(); + var rectangle = sut.PutNextRectangle(size); + } + + IsRectanglesIntersect(sut.Rectangles).Should().BeFalse(); + } +} \ No newline at end of file diff --git a/TagsCloudTests/GlobalUsings.cs b/TagsCloudTests/GlobalUsings.cs new file mode 100644 index 000000000..cefced496 --- /dev/null +++ b/TagsCloudTests/GlobalUsings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/TagsCloudTests/RectanglesVisualizerTests.cs b/TagsCloudTests/RectanglesVisualizerTests.cs new file mode 100644 index 000000000..daf6550ae --- /dev/null +++ b/TagsCloudTests/RectanglesVisualizerTests.cs @@ -0,0 +1,53 @@ +using System.Drawing; +using System.Drawing.Imaging; +using FluentAssertions; +using TagsCloud; + +namespace TagsCloudTests; + +public class RectanglesVisualizerTests +{ + private const int MinCoordinate = 0; + private const int MaxCoordinate = 5000; + private static readonly Random Random = new(); + + [Test] + public void GetTagsCloudImage_DrawImageWithoutRectangles_EmptyImage() + { + var rectangles = new List(); + var expected = BitmapToByteArray(new Bitmap(100, 100)); + var result = BitmapToByteArray(RectanglesVisualizer.GetTagsCloudImage(rectangles)); + result.Should().BeEquivalentTo(expected); + } + + [Test] + public void GetTagsCloudImage_DrawSomeRectangles_AllRectanglesInImage() + { + var count = 10; + var rectangles = new List(); + for (var i = 0; i < count; i++) + { + var locate = GetRandomLocation(); + var size = Utils.GetRandomSize(); + var rect = new Rectangle(locate, size); + rectangles.Add(rect); + } + + var image = RectanglesVisualizer.GetTagsCloudImage(rectangles); + CheckImageBorders(rectangles, image).Should().BeTrue(); + } + + private static Point GetRandomLocation() => + new Point(Random.Next(MinCoordinate, MaxCoordinate), Random.Next(MinCoordinate, MaxCoordinate)); + + private static byte[] BitmapToByteArray(Bitmap bitmap) + { + using var stream = new MemoryStream(); + bitmap.Save(stream, ImageFormat.Png); + return stream.ToArray(); + } + + private static bool CheckImageBorders(List rectangles, Bitmap image) => + rectangles.Max(rectangle => rectangle.Bottom) < image.Height && + rectangles.Max(rectangle => rectangle.Right) < image.Width; +} \ No newline at end of file diff --git a/TagsCloudTests/SpiralTests.cs b/TagsCloudTests/SpiralTests.cs new file mode 100644 index 000000000..53b3c50b4 --- /dev/null +++ b/TagsCloudTests/SpiralTests.cs @@ -0,0 +1,46 @@ +using System.Drawing; +using FluentAssertions; +using TagsCloud; + +namespace TagsCloudTests; + +public class SpiralTests +{ + private Point center; + + [SetUp] + public void Setup() + { + center = new Point(10, 10); + } + + private static IEnumerable ConstructorSpiralPoints => new[] + { + new TestCaseData(2.5f, + new Point[] { new(10, 10), new(5, 14), new(14, -2), new(16, 28), new(-11, -4), new(41, 8) }) + .SetName("AngleStep is positive"), + new TestCaseData(-2.5f, + new Point[] { new(10, 10), new(5, 6), new(14, 22), new(16, -8), new(-11, 24), new(41, 12) }) + .SetName("AngleStep is negative") + }; + + [Test] + public void Spiral_StepAngleEquals0_ShouldBeThrowException() + { + Action action = () => new Spiral(center, 0); + action.Should().Throw() + .WithMessage("the step must not be equal to 0"); + } + + [TestCaseSource(nameof(ConstructorSpiralPoints))] + public void Spiral_GetNextPoint_CreatePointsWithCustomAngle_ReturnsCorrectPoints(float angleStep, + Point[] expectedPoints) + { + var spiral = new Spiral(new Point(10, 10), angleStep); + var resultPoints = new Point[expectedPoints.Length]; + for (var i = 0; i < resultPoints.Length; i++) + resultPoints[i] = spiral.GetPoint(); + + resultPoints.Should().BeEquivalentTo(expectedPoints); + } +} \ No newline at end of file diff --git a/TagsCloudTests/TagsCloudTests.csproj b/TagsCloudTests/TagsCloudTests.csproj new file mode 100644 index 000000000..5a54db322 --- /dev/null +++ b/TagsCloudTests/TagsCloudTests.csproj @@ -0,0 +1,22 @@ + + + + net6-windows + enable + enable + + false + true + + + + + + + + + + + + + diff --git a/di.sln b/di.sln index b27b7c05d..f5d12022f 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}") = "TagsCloud", "TagsCloud\TagsCloud.csproj", "{6D9AE4E2-DAE6-4E87-9EA9-8E3067FDC3C1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudTests", "TagsCloudTests\TagsCloudTests.csproj", "{F705FB15-AC67-4F5D-AB3A-39BC83D3C6F7}" +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 + {6D9AE4E2-DAE6-4E87-9EA9-8E3067FDC3C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D9AE4E2-DAE6-4E87-9EA9-8E3067FDC3C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D9AE4E2-DAE6-4E87-9EA9-8E3067FDC3C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D9AE4E2-DAE6-4E87-9EA9-8E3067FDC3C1}.Release|Any CPU.Build.0 = Release|Any CPU + {F705FB15-AC67-4F5D-AB3A-39BC83D3C6F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F705FB15-AC67-4F5D-AB3A-39BC83D3C6F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F705FB15-AC67-4F5D-AB3A-39BC83D3C6F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F705FB15-AC67-4F5D-AB3A-39BC83D3C6F7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From a4788af2cdee9f483bf88b24adbe54dc75985177 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Tue, 23 Jan 2024 15:44:19 +0500 Subject: [PATCH 02/10] first working version without tests --- TagsCloud/Actions/CircleCloudAction.cs | 31 ++++++++++ TagsCloud/Actions/FlowerCloudAction.cs | 33 +++++++++++ TagsCloud/Actions/ImageSettingsAction.cs | 28 ++++++++++ TagsCloud/Actions/SaveFileAction.cs | 34 +++++++++++ TagsCloud/Actions/TagSettingsAction.cs | 22 ++++++++ TagsCloud/Actions/UploadFileAction.cs | 34 +++++++++++ .../Actions/WordAnalyzerSettingsAction.cs | 21 +++++++ TagsCloud/CloudForm.Designer.cs | 41 ++++++++++++++ TagsCloud/CloudForm.cs | 31 ++++++++++ ...cularCloudLayouter.cs => CloudLayouter.cs} | 12 ++-- TagsCloud/FileReader.cs | 24 ++++++++ TagsCloud/FlowerSpiral.cs | 32 +++++++++++ TagsCloud/ImageSaver.cs | 18 ++++++ TagsCloud/Infrastructure/AppSettings.cs | 6 ++ TagsCloud/Infrastructure/IImageHolder.cs | 14 +++++ .../Infrastructure/PictureBoxImageHolder.cs | 45 +++++++++++++++ TagsCloud/Infrastructure/SettingsForm.cs | 38 +++++++++++++ .../UiActions/EnumExtensions.cs | 18 ++++++ .../Infrastructure/UiActions/IUiAction.cs | 10 ++++ .../Infrastructure/UiActions/MenuCategory.cs | 16 ++++++ .../UiActions/UiActionExtensions.cs | 33 +++++++++++ TagsCloud/MainProgram.cs | 54 +++++++++--------- TagsCloud/Parsers/DocParser.cs | 14 +++++ TagsCloud/Parsers/DocxParser.cs | 13 +++++ TagsCloud/Parsers/IParser.cs | 10 ++++ TagsCloud/Parsers/ParserHelper.cs | 21 +++++++ TagsCloud/Parsers/TxtParser.cs | 13 +++++ TagsCloud/PartSpeech.cs | 13 +++++ TagsCloud/RectanglesVisualizer.cs | 56 ++++--------------- TagsCloud/Settings/ImageSettings.cs | 7 +++ TagsCloud/Settings/TagSettings.cs | 10 ++++ TagsCloud/Settings/WordAnalyzerSettings.cs | 13 +++++ TagsCloud/Tag.cs | 19 +++++++ TagsCloud/TagCloudPainter.cs | 39 +++++++++++++ TagsCloud/TagGenerator.cs | 28 ++++++++++ TagsCloud/TagsCloud.csproj | 22 ++++++++ TagsCloud/TagsCloud.csproj.DotSettings | 2 + TagsCloud/WordAnalyzer.cs | 43 ++++++++++++++ TagsCloud/WordAnalyzerHelper.cs | 21 +++++++ TagsCloud/WordInfo.cs | 20 +++++++ 40 files changed, 882 insertions(+), 77 deletions(-) create mode 100644 TagsCloud/Actions/CircleCloudAction.cs create mode 100644 TagsCloud/Actions/FlowerCloudAction.cs create mode 100644 TagsCloud/Actions/ImageSettingsAction.cs create mode 100644 TagsCloud/Actions/SaveFileAction.cs create mode 100644 TagsCloud/Actions/TagSettingsAction.cs create mode 100644 TagsCloud/Actions/UploadFileAction.cs create mode 100644 TagsCloud/Actions/WordAnalyzerSettingsAction.cs create mode 100644 TagsCloud/CloudForm.Designer.cs create mode 100644 TagsCloud/CloudForm.cs rename TagsCloud/{CircularCloudLayouter.cs => CloudLayouter.cs} (76%) create mode 100644 TagsCloud/FileReader.cs create mode 100644 TagsCloud/FlowerSpiral.cs create mode 100644 TagsCloud/ImageSaver.cs create mode 100644 TagsCloud/Infrastructure/AppSettings.cs create mode 100644 TagsCloud/Infrastructure/IImageHolder.cs create mode 100644 TagsCloud/Infrastructure/PictureBoxImageHolder.cs create mode 100644 TagsCloud/Infrastructure/SettingsForm.cs create mode 100644 TagsCloud/Infrastructure/UiActions/EnumExtensions.cs create mode 100644 TagsCloud/Infrastructure/UiActions/IUiAction.cs create mode 100644 TagsCloud/Infrastructure/UiActions/MenuCategory.cs create mode 100644 TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs create mode 100644 TagsCloud/Parsers/DocParser.cs create mode 100644 TagsCloud/Parsers/DocxParser.cs create mode 100644 TagsCloud/Parsers/IParser.cs create mode 100644 TagsCloud/Parsers/ParserHelper.cs create mode 100644 TagsCloud/Parsers/TxtParser.cs create mode 100644 TagsCloud/PartSpeech.cs create mode 100644 TagsCloud/Settings/ImageSettings.cs create mode 100644 TagsCloud/Settings/TagSettings.cs create mode 100644 TagsCloud/Settings/WordAnalyzerSettings.cs create mode 100644 TagsCloud/Tag.cs create mode 100644 TagsCloud/TagCloudPainter.cs create mode 100644 TagsCloud/TagGenerator.cs create mode 100644 TagsCloud/TagsCloud.csproj.DotSettings create mode 100644 TagsCloud/WordAnalyzer.cs create mode 100644 TagsCloud/WordAnalyzerHelper.cs create mode 100644 TagsCloud/WordInfo.cs diff --git a/TagsCloud/Actions/CircleCloudAction.cs b/TagsCloud/Actions/CircleCloudAction.cs new file mode 100644 index 000000000..dec564954 --- /dev/null +++ b/TagsCloud/Actions/CircleCloudAction.cs @@ -0,0 +1,31 @@ +using System.Drawing; +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; + +namespace TagsCloud.Actions; + +public class CircleCloudAction : IUiAction +{ + private IImageHolder imageHolder; + private TagCloudPainter painter; + private readonly AppSettings settings; + + public CircleCloudAction( + AppSettings appSettings, IImageHolder imageHolder, TagCloudPainter painter) + { + this.settings = appSettings; + this.imageHolder = imageHolder; + this.painter = painter; + } + public MenuCategory Category => MenuCategory.Types; + public string Name => "Круг"; + public string Description => ""; + public void Perform() + { + if (settings.File == null){ + throw new Exception("сначала загрузи файл"); + } + var size = imageHolder.GetImageSize(); + painter.Paint(new Spiral(new Point(size.Width/2, size.Height/2)), settings.File.FullName); + } +} \ No newline at end of file diff --git a/TagsCloud/Actions/FlowerCloudAction.cs b/TagsCloud/Actions/FlowerCloudAction.cs new file mode 100644 index 000000000..7bdd0e93c --- /dev/null +++ b/TagsCloud/Actions/FlowerCloudAction.cs @@ -0,0 +1,33 @@ +using System.Drawing; +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; + +namespace TagsCloud.Actions; + +public class FlowerCloudAction : IUiAction +{ + private readonly IImageHolder imageHolder; + private readonly TagCloudPainter painter; + private readonly AppSettings settings; + + public FlowerCloudAction( + IImageHolder imageHolder, TagCloudPainter painter, AppSettings settings) + { + this.settings = settings; + this.imageHolder = imageHolder; + this.painter = painter; + } + + public void Perform() + { + if (settings.File == null){ + throw new Exception("сначала загрузи файл"); + } + var size = imageHolder.GetImageSize(); + painter.Paint(new FlowerSpiral(new Point(size.Width/2, size.Height/2), 0.5, 4), settings.File.FullName); + } + + public MenuCategory Category => MenuCategory.Types; + public string Name => "Цветок"; + public string Description => ""; +} \ No newline at end of file diff --git a/TagsCloud/Actions/ImageSettingsAction.cs b/TagsCloud/Actions/ImageSettingsAction.cs new file mode 100644 index 000000000..a240113f5 --- /dev/null +++ b/TagsCloud/Actions/ImageSettingsAction.cs @@ -0,0 +1,28 @@ +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; +using TagsCloud.Settings; + +namespace TagsCloud.Actions; + +public class ImageSettingsAction : IUiAction +{ + private readonly IImageHolder imageHolder; + private readonly ImageSettings imageSettings; + + public ImageSettingsAction(IImageHolder imageHolder, + ImageSettings imageSettings) + { + this.imageHolder = imageHolder; + this.imageSettings = imageSettings; + } + + public MenuCategory Category => MenuCategory.Settings; + public string Name => "Изображение..."; + public string Description => "Размеры изображения"; + + public void Perform() + { + SettingsForm.For(imageSettings).ShowDialog(); + imageHolder.RecreateImage(imageSettings); + } +} \ No newline at end of file diff --git a/TagsCloud/Actions/SaveFileAction.cs b/TagsCloud/Actions/SaveFileAction.cs new file mode 100644 index 000000000..97f680d95 --- /dev/null +++ b/TagsCloud/Actions/SaveFileAction.cs @@ -0,0 +1,34 @@ +using System.Windows.Forms; +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; + +namespace TagsCloud.Actions; + +public class SaveFileAction : IUiAction +{ + private readonly IImageHolder imageHolder; + + public SaveFileAction(IImageHolder imageHolder) + { + this.imageHolder = imageHolder; + } + public MenuCategory Category => MenuCategory.File; + public string Name => "Сохранить"; + public string Description => "Сохранить изображение в файл"; + public void Perform() + { + var imagesDirectoryPath = Path.GetFullPath("..//..//..//images"); + if (!Directory.Exists(imagesDirectoryPath)) Directory.CreateDirectory(imagesDirectoryPath); + var dialog = new SaveFileDialog + { + CheckFileExists = false, + InitialDirectory = Path.GetFullPath(imagesDirectoryPath), + DefaultExt = "png", + FileName = "image.png", + Filter = "Изображения (*.png)|*.png" + }; + var res = dialog.ShowDialog(); + if (res == DialogResult.OK) + imageHolder.SaveImage(dialog.FileName); + } +} \ No newline at end of file diff --git a/TagsCloud/Actions/TagSettingsAction.cs b/TagsCloud/Actions/TagSettingsAction.cs new file mode 100644 index 000000000..b31740a03 --- /dev/null +++ b/TagsCloud/Actions/TagSettingsAction.cs @@ -0,0 +1,22 @@ +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; +using TagsCloud.Settings; + +namespace TagsCloud.Actions; + +public class TagSettingsAction : IUiAction +{ + private readonly TagSettings tag; + + public TagSettingsAction(TagSettings tag) + { + this.tag = tag; + } + public MenuCategory Category => MenuCategory.Settings; + public string Name => "Облако тегов..."; + public string Description => ""; + public void Perform() + { + SettingsForm.For(tag).ShowDialog(); + } +} \ No newline at end of file diff --git a/TagsCloud/Actions/UploadFileAction.cs b/TagsCloud/Actions/UploadFileAction.cs new file mode 100644 index 000000000..30147f112 --- /dev/null +++ b/TagsCloud/Actions/UploadFileAction.cs @@ -0,0 +1,34 @@ +using System.Windows.Forms; +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; + +namespace TagsCloud.Actions; + +public class UploadFileAction : IUiAction +{ + private readonly IImageHolder imageHolder; + private readonly AppSettings settings; + + public UploadFileAction(IImageHolder imageHolder, AppSettings settings) + { + this.settings = settings; + this.imageHolder = imageHolder; + } + public MenuCategory Category => MenuCategory.File; + public string Name => "Загрузить"; + public string Description => ""; + + public void Perform() + { + var openFileDialog = new OpenFileDialog + { + Title = "Выберите файл", + Filter = "Текстовые файлы (*.txt)|*.txt|Документы (*.doc;*.docx)|*.doc;*.docx|Все файлы (*.*)|*.*", + FilterIndex = 1, + RestoreDirectory = true, + }; + if (openFileDialog.ShowDialog() != DialogResult.OK) return; + settings.File = new FileInfo(openFileDialog.FileName); + + } +} \ No newline at end of file diff --git a/TagsCloud/Actions/WordAnalyzerSettingsAction.cs b/TagsCloud/Actions/WordAnalyzerSettingsAction.cs new file mode 100644 index 000000000..497a6c22f --- /dev/null +++ b/TagsCloud/Actions/WordAnalyzerSettingsAction.cs @@ -0,0 +1,21 @@ +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; + +namespace TagsCloud.Actions; + +public class WordAnalyzerSettingsAction : IUiAction +{ + private readonly WordAnalyzerSettings wordAnalyzerSettings; + + public WordAnalyzerSettingsAction(WordAnalyzerSettings wordAnalyzerSettings) + { + this.wordAnalyzerSettings = wordAnalyzerSettings; + } + public MenuCategory Category => MenuCategory.Settings; + public string Name => "Анализатор..."; + public string Description => ""; + public void Perform() + { + SettingsForm.For(wordAnalyzerSettings).ShowDialog(); + } +} \ No newline at end of file diff --git a/TagsCloud/CloudForm.Designer.cs b/TagsCloud/CloudForm.Designer.cs new file mode 100644 index 000000000..a1deb3404 --- /dev/null +++ b/TagsCloud/CloudForm.Designer.cs @@ -0,0 +1,41 @@ +using System.ComponentModel; + +namespace TagsCloud; + +partial class CloudForm +{ + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Text = "CloudForm"; + } + + #endregion +} \ No newline at end of file diff --git a/TagsCloud/CloudForm.cs b/TagsCloud/CloudForm.cs new file mode 100644 index 000000000..4cdffeb35 --- /dev/null +++ b/TagsCloud/CloudForm.cs @@ -0,0 +1,31 @@ +using System.Drawing; +using System.Windows.Forms; +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; +using TagsCloud.Settings; + +namespace TagsCloud; + +public partial class CloudForm : Form +{ + public CloudForm(IUiAction[] actions, + PictureBoxImageHolder pictureBox, + ImageSettings imageSettings) + { + InitializeComponent(); + ClientSize = new Size(imageSettings.Width, imageSettings.Height); + + var mainMenu = new MenuStrip(); + mainMenu.Items.AddRange(actions.ToMenuItems()); + Controls.Add(mainMenu); + + pictureBox.RecreateImage(imageSettings); + pictureBox.Dock = DockStyle.Fill; + Controls.Add(pictureBox); + } + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + Text = "TagCloud Painter"; + } +} \ No newline at end of file diff --git a/TagsCloud/CircularCloudLayouter.cs b/TagsCloud/CloudLayouter.cs similarity index 76% rename from TagsCloud/CircularCloudLayouter.cs rename to TagsCloud/CloudLayouter.cs index b0dd0a203..7b7d6b994 100644 --- a/TagsCloud/CircularCloudLayouter.cs +++ b/TagsCloud/CloudLayouter.cs @@ -2,13 +2,13 @@ namespace TagsCloud; -public class CircularCloudLayouter : ICircularCloudLayouter +public class CloudLayouter : ICircularCloudLayouter { public List Rectangles { get; } private readonly ISpiral spiral; - public CircularCloudLayouter(ISpiral spiral) + public CloudLayouter(ISpiral spiral) { this.spiral = spiral; Rectangles = new List(); @@ -26,14 +26,14 @@ public Rectangle PutNextRectangle(Size rectangleSize) private Rectangle CreateNextRectangle(Size rectangleSize) { var point = spiral.GetPoint(); - var rectangles = new Rectangle(point, rectangleSize); - while (!HasNoIntersections(rectangles)) + var rectangle = new Rectangle(point, rectangleSize); + while (!HasNoIntersections(rectangle)) { point = spiral.GetPoint(); - rectangles = new Rectangle(point, rectangleSize); + rectangle = new Rectangle(point, rectangleSize); } - return rectangles; + return rectangle; } private bool HasNoIntersections(Rectangle rectangles) diff --git a/TagsCloud/FileReader.cs b/TagsCloud/FileReader.cs new file mode 100644 index 000000000..61279317d --- /dev/null +++ b/TagsCloud/FileReader.cs @@ -0,0 +1,24 @@ +using System.IO; +using Spire.Doc; + +namespace TagsCloud; + +public class FileReader +{ + private readonly Dictionary parsers; + + public FileReader(IEnumerable parsers) + { + this.parsers = parsers.ToDictionary(parser => parser.FileType); + } + + public IEnumerable GetWords(string filePath) + { + var fileType = Path.GetExtension(filePath).Trim('.').ToLower(); + if (!parsers.ContainsKey(fileType)){ + throw new ArgumentException($"not found parser for {fileType}"); + } + + return parsers[fileType].GetWordList(filePath); + } +} \ No newline at end of file diff --git a/TagsCloud/FlowerSpiral.cs b/TagsCloud/FlowerSpiral.cs new file mode 100644 index 000000000..9c0608206 --- /dev/null +++ b/TagsCloud/FlowerSpiral.cs @@ -0,0 +1,32 @@ +using System.Drawing; + +namespace TagsCloud; + +public class FlowerSpiral : ISpiral +{ + private int counter; + private readonly float step; + private readonly Point center; + private int petalCount; + private double petalLength; + + public FlowerSpiral(Point center, double petalLength = 0.25, int petalCount = 1, float step = 0.1f) + { + this.center = center; + this.petalLength = petalLength; + this.petalCount = petalCount; + if (step == 0) + throw new ArgumentException($"the {nameof(petalCount)} must not be equal to 0"); + this.step = step; + } + public Point GetPoint() + { + var angle = step * counter; + var radius = angle * petalLength * Math.Sin(petalCount * angle); + var xOffset = (float)(radius * Math.Cos(angle)); + var yOffset = (float)(radius * Math.Sin(angle)); + var point = new Point((int)Math.Round(center.X + xOffset), (int)Math.Round(center.Y + yOffset)); + counter += 1; + return point; + } +} \ No newline at end of file diff --git a/TagsCloud/ImageSaver.cs b/TagsCloud/ImageSaver.cs new file mode 100644 index 000000000..a6fddc3b4 --- /dev/null +++ b/TagsCloud/ImageSaver.cs @@ -0,0 +1,18 @@ +using System.Drawing.Imaging; + +namespace TagsCloud; + +public static class ImageSaver +{ + public static void SaveImage(string workingDirectory, List tags, string imageName) + { + /* var imagesDirectoryPath = Path.Combine(workingDirectory, "images"); + + if (!Directory.Exists(imagesDirectoryPath)) Directory.CreateDirectory(imagesDirectoryPath); + + var imagePath = Path.Combine(imagesDirectoryPath, $"{imageName}.png"); + + using var image = RectanglesVisualizer.RenderCloudImage(tags); + image.Save(imagePath, ImageFormat.Png); +*/ } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/AppSettings.cs b/TagsCloud/Infrastructure/AppSettings.cs new file mode 100644 index 000000000..f03b8bafb --- /dev/null +++ b/TagsCloud/Infrastructure/AppSettings.cs @@ -0,0 +1,6 @@ +namespace TagsCloud.Infrastructure; + +public class AppSettings +{ + public FileInfo File { get; set; } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/IImageHolder.cs b/TagsCloud/Infrastructure/IImageHolder.cs new file mode 100644 index 000000000..1ee763031 --- /dev/null +++ b/TagsCloud/Infrastructure/IImageHolder.cs @@ -0,0 +1,14 @@ +using System.Drawing; +using TagsCloud.Settings; + +namespace TagsCloud.Infrastructure +{ + public interface IImageHolder + { + Size GetImageSize(); + Graphics StartDrawing(); + void UpdateUi(); + void RecreateImage(ImageSettings settings); + void SaveImage(string fileName); + } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/PictureBoxImageHolder.cs b/TagsCloud/Infrastructure/PictureBoxImageHolder.cs new file mode 100644 index 000000000..ba463bb8c --- /dev/null +++ b/TagsCloud/Infrastructure/PictureBoxImageHolder.cs @@ -0,0 +1,45 @@ +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.Windows.Forms; +using TagsCloud.Settings; + +namespace TagsCloud.Infrastructure; + +public class PictureBoxImageHolder : PictureBox, IImageHolder +{ + public Size GetImageSize() + { + FailIfNotInitialized(); + return Image.Size; + } + + public Graphics StartDrawing() + { + FailIfNotInitialized(); + return Graphics.FromImage(Image); + } + + private void FailIfNotInitialized() + { + if (Image == null) + throw new InvalidOperationException("Call PictureBoxImageHolder.RecreateImage before other method call!"); + } + + public void UpdateUi() + { + Refresh(); + Application.DoEvents(); + } + + public void RecreateImage(ImageSettings imageSettings) + { + Image = new Bitmap(imageSettings.Width, imageSettings.Height, PixelFormat.Format24bppRgb); + } + + public void SaveImage(string fileName) + { + FailIfNotInitialized(); + Image.Save(fileName); + } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/SettingsForm.cs b/TagsCloud/Infrastructure/SettingsForm.cs new file mode 100644 index 000000000..da9bd1593 --- /dev/null +++ b/TagsCloud/Infrastructure/SettingsForm.cs @@ -0,0 +1,38 @@ +using System.Windows.Forms; + +namespace TagsCloud.Infrastructure +{ + public static class SettingsForm + { + public static SettingsForm For(TSettings settings) + { + return new SettingsForm(settings); + } + } + + public class SettingsForm : Form + { + public SettingsForm(TSettings settings) + { + var okButton = new Button + { + Text = "OK", + DialogResult = DialogResult.OK, + Dock = DockStyle.Bottom, + }; + Controls.Add(okButton); + Controls.Add(new PropertyGrid + { + SelectedObject = settings, + Dock = DockStyle.Fill + }); + AcceptButton = okButton; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + Text = "Настройки"; + } + } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/EnumExtensions.cs b/TagsCloud/Infrastructure/UiActions/EnumExtensions.cs new file mode 100644 index 000000000..23994225e --- /dev/null +++ b/TagsCloud/Infrastructure/UiActions/EnumExtensions.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace TagsCloud.Infrastructure.UiActions +{ + public static class EnumExtensions + { + public static string GetDescription(this Enum enumValue) + { + var fieldInfo = enumValue.GetType().GetField(enumValue.ToString()); + var description = fieldInfo + .GetCustomAttributes(typeof(DescriptionAttribute), false) + .Cast() + .FirstOrDefault()?.Description; + + return description ?? enumValue.ToString(); + } + } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/IUiAction.cs b/TagsCloud/Infrastructure/UiActions/IUiAction.cs new file mode 100644 index 000000000..f0ae383f7 --- /dev/null +++ b/TagsCloud/Infrastructure/UiActions/IUiAction.cs @@ -0,0 +1,10 @@ +namespace TagsCloud.Infrastructure.UiActions +{ + public interface IUiAction + { + MenuCategory Category { get; } + string Name { get; } + string Description { get; } + void Perform(); + } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/MenuCategory.cs b/TagsCloud/Infrastructure/UiActions/MenuCategory.cs new file mode 100644 index 000000000..6a4a2603a --- /dev/null +++ b/TagsCloud/Infrastructure/UiActions/MenuCategory.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; + +namespace TagsCloud.Infrastructure.UiActions +{ + public enum MenuCategory + { + [Description("Файл")] + File = 0, + + [Description("Типы Облаков")] + Types = 1, + + [Description("Настойки")] + Settings = 2, + } +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs b/TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs new file mode 100644 index 000000000..57e9cf900 --- /dev/null +++ b/TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs @@ -0,0 +1,33 @@ +using System.Windows.Forms; + +namespace TagsCloud.Infrastructure.UiActions +{ + public static class UiActionExtensions + { + public static ToolStripItem[] ToMenuItems(this IUiAction[] actions) + { + var items = actions.GroupBy(a => a.Category) + .OrderBy(a => a.Key) + .Select(g => CreateTopLevelMenuItem(g.Key, g.ToList())) + .Cast() + .ToArray(); + return items; + } + + private static ToolStripMenuItem CreateTopLevelMenuItem(MenuCategory category, IList items) + { + var menuItems = items.Select(a => a.ToMenuItem()).ToArray(); + return new ToolStripMenuItem(category.GetDescription(), null, menuItems); + } + + public static ToolStripItem ToMenuItem(this IUiAction action) + { + return + new ToolStripMenuItem(action.Name, null, (sender, args) => action.Perform()) + { + ToolTipText = action.Description, + Tag = action + }; + } + } +} \ No newline at end of file diff --git a/TagsCloud/MainProgram.cs b/TagsCloud/MainProgram.cs index a19762d5b..5f44b72b6 100644 --- a/TagsCloud/MainProgram.cs +++ b/TagsCloud/MainProgram.cs @@ -1,34 +1,38 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Drawing; -using System.Drawing.Imaging; + +using System.Reflection; +using System.Windows.Forms; +using Autofac; +using TagsCloud.Actions; +using TagsCloud.Infrastructure; +using TagsCloud.Infrastructure.UiActions; +using TagsCloud.Settings; namespace TagsCloud; public class MainProgram { + [STAThread] public static void Main(string[] args) { - var spiral = new Spiral(new Point(100, 100)); - var layout = new CircularCloudLayouter(spiral); - - for (var i = 0; i < 10000; i++) - { - var rectangle = layout.PutNextRectangle(Utils.GetRandomSize()); - } - - var workingDirectory = Environment.CurrentDirectory; - var imagesDirectoryPath = Path.Combine(workingDirectory, "images"); - - if (!Directory.Exists(imagesDirectoryPath)) - { - Directory.CreateDirectory(imagesDirectoryPath); - } - - const string imageName = "1077rect"; - var imagePath = Path.Combine(imagesDirectoryPath, $"{imageName}.png"); - - using var image = RectanglesVisualizer.GetTagsCloudImage(layout.Rectangles); - image.Save(imagePath, ImageFormat.Png); + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + var containerBuilder = new ContainerBuilder(); + containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) + .Where(t => typeof(IUiAction).IsAssignableFrom(t)) + .AsImplementedInterfaces(); + containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) + .Where(t => typeof(IParser).IsAssignableFrom(t)) + .AsImplementedInterfaces(); + containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.RegisterType(); + containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.RegisterType().As().SingleInstance(); + containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.RegisterType().AsSelf().SingleInstance(); + var cloudForm = containerBuilder.Build().Resolve(); + Application.Run(cloudForm); } } \ No newline at end of file diff --git a/TagsCloud/Parsers/DocParser.cs b/TagsCloud/Parsers/DocParser.cs new file mode 100644 index 000000000..e844b9f46 --- /dev/null +++ b/TagsCloud/Parsers/DocParser.cs @@ -0,0 +1,14 @@ +using Spire.Doc; + +namespace TagsCloud; + +public class DocParser : IParser +{ + public string FileType => "doc"; + + public IEnumerable GetWordList(string filePath) + { + var document = new Document(filePath, FileFormat.Doc); + return ParserHelper.GetTextParagraph(document); + } +} \ No newline at end of file diff --git a/TagsCloud/Parsers/DocxParser.cs b/TagsCloud/Parsers/DocxParser.cs new file mode 100644 index 000000000..c30d49350 --- /dev/null +++ b/TagsCloud/Parsers/DocxParser.cs @@ -0,0 +1,13 @@ +using Spire.Doc; + +namespace TagsCloud; + +public class DocxParser : IParser +{ + public string FileType => "docx"; + public IEnumerable GetWordList(string filePath) + { + var document = new Document(filePath, FileFormat.Docx); + return ParserHelper.GetTextParagraph(document); + } +} \ No newline at end of file diff --git a/TagsCloud/Parsers/IParser.cs b/TagsCloud/Parsers/IParser.cs new file mode 100644 index 000000000..705e3692a --- /dev/null +++ b/TagsCloud/Parsers/IParser.cs @@ -0,0 +1,10 @@ +using Spire.Doc; + +namespace TagsCloud; + +public interface IParser +{ + public string FileType { get; } + + public IEnumerable GetWordList(string filePath); +} \ No newline at end of file diff --git a/TagsCloud/Parsers/ParserHelper.cs b/TagsCloud/Parsers/ParserHelper.cs new file mode 100644 index 000000000..de09d6a63 --- /dev/null +++ b/TagsCloud/Parsers/ParserHelper.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; +using Spire.Doc; +using Spire.Doc.Interface; + +namespace TagsCloud; + +public static class ParserHelper +{ + private static Regex SelectAllWordsRegex => new(@"([\w]+)", RegexOptions.Compiled); + public static IEnumerable GetTextParagraph(IDocument document) + { + var section = document.Sections[0]; + for (var i = 0; i < section.Paragraphs.Count; i++) + { + foreach (var word in SelectAllWordsRegex.Matches(section.Paragraphs[i].Text)) + { + yield return word.ToString(); + } + } + } +} \ No newline at end of file diff --git a/TagsCloud/Parsers/TxtParser.cs b/TagsCloud/Parsers/TxtParser.cs new file mode 100644 index 000000000..5371f0c58 --- /dev/null +++ b/TagsCloud/Parsers/TxtParser.cs @@ -0,0 +1,13 @@ +using Spire.Doc; + +namespace TagsCloud; + +public class TxtParser : IParser +{ + public string FileType => "txt"; + public IEnumerable GetWordList(string filePath) + { + var document = new Document(filePath, FileFormat.Txt); + return ParserHelper.GetTextParagraph(document); + } +} \ No newline at end of file diff --git a/TagsCloud/PartSpeech.cs b/TagsCloud/PartSpeech.cs new file mode 100644 index 000000000..c6cc36f0f --- /dev/null +++ b/TagsCloud/PartSpeech.cs @@ -0,0 +1,13 @@ +namespace TagsCloud; + +public enum PartSpeech +{ + Noun, + Pronoun, + Verb, + Adjective, + Adverb, + Preposition, + Conjunction, + Interjection +} \ No newline at end of file diff --git a/TagsCloud/RectanglesVisualizer.cs b/TagsCloud/RectanglesVisualizer.cs index 2bc61a85f..9464c7ed7 100644 --- a/TagsCloud/RectanglesVisualizer.cs +++ b/TagsCloud/RectanglesVisualizer.cs @@ -1,62 +1,26 @@ using System.Drawing; +using TagsCloud.Infrastructure; namespace TagsCloud; public static class RectanglesVisualizer { - private const int Border = 50; - public static Bitmap GetTagsCloudImage(List rectangles) + public static void RenderCloudImage(List tags, Graphics graphics, IImageHolder imageHolder) { - if (!rectangles.Any()) return new Bitmap(100, 100); - - var extremePoints = GetRectanglesExtremePoints(rectangles); - var sizeImage = GetImageSize(extremePoints); - var shift = GetRectanglesShift(extremePoints); - var image = new Bitmap(sizeImage.Width, sizeImage.Height); - using var graphics = Graphics.FromImage(image); + var sizeImage = imageHolder.GetImageSize(); var background = new SolidBrush(Color.Black); - graphics.FillRectangle(background, new Rectangle(0, 0, image.Width, image.Height)); - DrawTagsCloud(rectangles, graphics, shift); - - return image; - } - - private static (int Left, int Right, int Top, int Bottom) GetRectanglesExtremePoints(List rectangles) - { - var leftmost = rectangles.Min(rectangle => rectangle.Left); - var rightmost = rectangles.Max(rectangle => rectangle.Right); - var topmost = rectangles.Min(rectangle => rectangle.Top); - var bottommost = rectangles.Max(rectangle => rectangle.Bottom); - - return (leftmost, rightmost, topmost, bottommost); - } - - private static Point GetRectanglesShift((int Left, int Right, int Top, int Bottom) extremePoints) - { - var startX = extremePoints.Top >= 0 ? 0 : extremePoints.Top; - var startY = extremePoints.Left >= 0 ? 0 : extremePoints.Left; - - return new Point(Math.Abs(startX) + Border, Math.Abs(startY) + Border); - } - - - private static Size GetImageSize((int Left, int Right, int Top, int Bottom) extremePoints) - { - var height = Math.Abs(extremePoints.Bottom) + Math.Abs(extremePoints.Top) + 2 * Border; - var width = Math.Abs(extremePoints.Right) + Math.Abs(extremePoints.Left) + 2 * Border; - - return new Size(width, height); + graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); + DrawTagsCloud(tags, graphics); } - private static void DrawTagsCloud(List rectangles, Graphics graphics, Point shift) + private static void DrawTagsCloud(List tags, Graphics graphics) { - foreach (var rectangle in rectangles) + foreach (var tag in tags) { - var renderedRectangle = - new Rectangle(new Point(rectangle.X + shift.X, rectangle.Y + shift.Y), rectangle.Size); - using var pen = new Pen(Utils.GetRandomColor()); - graphics.DrawRectangle(pen, renderedRectangle); + var brush = new SolidBrush(tag.Color); + graphics.DrawString(tag.Word, tag.Font, brush, tag.Rectangle.Location); + } } } \ No newline at end of file diff --git a/TagsCloud/Settings/ImageSettings.cs b/TagsCloud/Settings/ImageSettings.cs new file mode 100644 index 000000000..e921b9c8c --- /dev/null +++ b/TagsCloud/Settings/ImageSettings.cs @@ -0,0 +1,7 @@ +namespace TagsCloud.Settings; + +public class ImageSettings +{ + public int Width { get; set; } = 1000; + public int Height { get; set; } = 1000; +} \ No newline at end of file diff --git a/TagsCloud/Settings/TagSettings.cs b/TagsCloud/Settings/TagSettings.cs new file mode 100644 index 000000000..52133612b --- /dev/null +++ b/TagsCloud/Settings/TagSettings.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagsCloud.Settings; + +public class TagSettings +{ + public string FontFamily { get; set; } = "Arial"; + public int Size { get; set; } = 1; + public Color Color { get; set; } = Color.DarkBlue; +} \ No newline at end of file diff --git a/TagsCloud/Settings/WordAnalyzerSettings.cs b/TagsCloud/Settings/WordAnalyzerSettings.cs new file mode 100644 index 000000000..f8bfcb054 --- /dev/null +++ b/TagsCloud/Settings/WordAnalyzerSettings.cs @@ -0,0 +1,13 @@ +namespace TagsCloud; + +public class WordAnalyzerSettings +{ + public List BoringWords { get; set; } = new(); + + public List ExcludedSpeeches { get; set; } = new() + { + PartSpeech.Interjection, PartSpeech.Preposition, + }; + + public List SelectedSpeeches { get; set; } = new(); +} \ No newline at end of file diff --git a/TagsCloud/Tag.cs b/TagsCloud/Tag.cs new file mode 100644 index 000000000..5c8332e3a --- /dev/null +++ b/TagsCloud/Tag.cs @@ -0,0 +1,19 @@ +using System.Drawing; + +namespace TagsCloud; + +public class Tag +{ + public readonly Font Font; + public readonly string Word; + public Rectangle Rectangle; + public Color Color; + + public Tag(Font font,string word, Rectangle rectangle, Color color) + { + Color = color; + Font = font; + Word = word; + Rectangle = rectangle; + } +} \ No newline at end of file diff --git a/TagsCloud/TagCloudPainter.cs b/TagsCloud/TagCloudPainter.cs new file mode 100644 index 000000000..0663c3006 --- /dev/null +++ b/TagsCloud/TagCloudPainter.cs @@ -0,0 +1,39 @@ +using Castle.Core.Internal; +using DeepMorphy; +using TagsCloud.Infrastructure; +using TagsCloud.Settings; + +namespace TagsCloud; + +public class TagCloudPainter +{ + + private readonly IImageHolder imageHolder; + private readonly TagSettings tagSettings; + private readonly WordAnalyzerSettings wordAnalyzerSettings; + private readonly FileReader fileReader; + + public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAnalyzerSettings wordAnalyzerSettings, + FileReader reader) + { + this.imageHolder = imageHolder; + this.tagSettings = tagSettings; + this.wordAnalyzerSettings = wordAnalyzerSettings; + fileReader = reader; + } + + public void Paint(ISpiral spiral, string path) + { + using (var graphics = imageHolder.StartDrawing()) + { + var wordAnalyzer = new WordAnalyzer(wordAnalyzerSettings); + var layout = new CloudLayouter(spiral); + var wordList = fileReader.GetWords(path); + var parsedList = wordAnalyzer.GetFrequencyList(wordList); + var tagGenerator = new TagGenerator(layout, tagSettings); + RectanglesVisualizer.RenderCloudImage(tagGenerator.GetTagsList(parsedList).ToList(), graphics, imageHolder); + imageHolder.UpdateUi(); + } + imageHolder.UpdateUi(); + } +} \ No newline at end of file diff --git a/TagsCloud/TagGenerator.cs b/TagsCloud/TagGenerator.cs new file mode 100644 index 000000000..8b6cd50d6 --- /dev/null +++ b/TagsCloud/TagGenerator.cs @@ -0,0 +1,28 @@ +using System.Drawing; +using System.Windows.Forms; +using TagsCloud.Settings; + +namespace TagsCloud; + +public class TagGenerator +{ + private readonly ICircularCloudLayouter cloudLayouter; + private readonly TagSettings settings; + public TagGenerator(ICircularCloudLayouter cloudLayouter, TagSettings settings) + { + this.cloudLayouter = cloudLayouter; + this.settings = settings; + + } + + public IEnumerable GetTagsList(IEnumerable wordInfos) + { + foreach (var info in wordInfos) + { + var font = new Font(settings.FontFamily, info.Count * settings.Size); + var textSize = TextRenderer.MeasureText(info.Word, font); + var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); + yield return new Tag(font, info.Word, textRectangle, settings.Color); + } + } +} \ No newline at end of file diff --git a/TagsCloud/TagsCloud.csproj b/TagsCloud/TagsCloud.csproj index 06eb4efbb..ba08daa00 100644 --- a/TagsCloud/TagsCloud.csproj +++ b/TagsCloud/TagsCloud.csproj @@ -9,7 +9,29 @@ + + + + + + ..\..\..\..\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\3.1.32\System.Windows.Forms.dll + + + + + + Form + + + Component + + + + + + + diff --git a/TagsCloud/TagsCloud.csproj.DotSettings b/TagsCloud/TagsCloud.csproj.DotSettings new file mode 100644 index 000000000..7eefcc1b9 --- /dev/null +++ b/TagsCloud/TagsCloud.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/TagsCloud/WordAnalyzer.cs b/TagsCloud/WordAnalyzer.cs new file mode 100644 index 000000000..aff15459b --- /dev/null +++ b/TagsCloud/WordAnalyzer.cs @@ -0,0 +1,43 @@ +using DeepMorphy; + +namespace TagsCloud; + +public class WordAnalyzer +{ + private readonly List ExcludedSpeeches; + private readonly List SelectedSpeeches; + private readonly List BoringWords; + + public WordAnalyzer(WordAnalyzerSettings settings) + { + ExcludedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(settings.ExcludedSpeeches); + SelectedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(settings.SelectedSpeeches); + BoringWords = settings.BoringWords; + + } + + private IEnumerable GetFilteredWords(IEnumerable words) + { + var morphAnalyzer = new MorphAnalyzer(true); + var morphInfos = morphAnalyzer.Parse(words); + return morphInfos.Where(morphInfo => !BoringWords.Contains(morphInfo.BestTag.Lemma) && morphInfo.Tags.All(tag => + !ExcludedSpeeches.Contains(tag["чр"]) && + (SelectedSpeeches.Count == 0 || + SelectedSpeeches + .Contains(tag["чр"])))) + .Select(info => info.BestTag.HasLemma ? info.BestTag.Lemma : info.Text); + } + + public IEnumerable GetFrequencyList(IEnumerable words) + { + var parsedWords = new Dictionary(); + foreach (var word in GetFilteredWords(words)) + { + parsedWords.TryAdd(word, 0); + parsedWords[word]++; + } + + return parsedWords.Select(x => WordInfo.Create(x.Key, x.Value)).OrderByDescending(info => info.Count) + .ThenByDescending(info => info.Word); + } +} \ No newline at end of file diff --git a/TagsCloud/WordAnalyzerHelper.cs b/TagsCloud/WordAnalyzerHelper.cs new file mode 100644 index 000000000..9d838ef43 --- /dev/null +++ b/TagsCloud/WordAnalyzerHelper.cs @@ -0,0 +1,21 @@ +namespace TagsCloud; + +public static class WordAnalyzerHelper +{ + private static readonly Dictionary converter = new() + { + { PartSpeech.Noun, "сущ" }, + { PartSpeech.Pronoun, "мест" }, + { PartSpeech.Verb, "гл" }, + { PartSpeech.Adjective, "прил" }, + { PartSpeech.Conjunction, "союз" }, + { PartSpeech.Adverb, "нареч" }, + { PartSpeech.Preposition, "предл" }, + { PartSpeech.Interjection, "межд" } + }; + + public static List GetConvertedSpeeches(IEnumerable speeches) + { + return speeches.Select(speech => converter[speech]).ToList(); + } +} \ No newline at end of file diff --git a/TagsCloud/WordInfo.cs b/TagsCloud/WordInfo.cs new file mode 100644 index 000000000..bd23aa16e --- /dev/null +++ b/TagsCloud/WordInfo.cs @@ -0,0 +1,20 @@ +using System.Windows.Forms; + +namespace TagsCloud; + +public class WordInfo +{ + public readonly int Count; + public readonly string Word; + + private WordInfo(string word, int count) + { + Word = word; + Count = count; + } + + public static WordInfo Create(string word, int count) + { + return new WordInfo(word, count); + } +} \ No newline at end of file From 3002ccf39fa36432342f4e2cd1079e1aa2c4dfbc Mon Sep 17 00:00:00 2001 From: PavelUd Date: Wed, 24 Jan 2024 15:46:03 +0500 Subject: [PATCH 03/10] add some tests --- TagsCloud/Actions/CircleCloudAction.cs | 16 +-- TagsCloudTests/AnalyzerTests.cs | 99 +++++++++++++++++++ TagsCloudTests/FileReaderTestData.cs | 23 +++++ TagsCloudTests/FileReaderTests.cs | 69 +++++++++++++ TagsCloudTests/FlowerSpiralTests.cs | 53 ++++++++++ ...CloudLayouterTests.cs => LayouterTests.cs} | 30 +----- TagsCloudTests/RectanglesVisualizerTests.cs | 53 ---------- 7 files changed, 255 insertions(+), 88 deletions(-) create mode 100644 TagsCloudTests/AnalyzerTests.cs create mode 100644 TagsCloudTests/FileReaderTestData.cs create mode 100644 TagsCloudTests/FileReaderTests.cs create mode 100644 TagsCloudTests/FlowerSpiralTests.cs rename TagsCloudTests/{CircularCloudLayouterTests.cs => LayouterTests.cs} (62%) delete mode 100644 TagsCloudTests/RectanglesVisualizerTests.cs diff --git a/TagsCloud/Actions/CircleCloudAction.cs b/TagsCloud/Actions/CircleCloudAction.cs index dec564954..44e900e9a 100644 --- a/TagsCloud/Actions/CircleCloudAction.cs +++ b/TagsCloud/Actions/CircleCloudAction.cs @@ -6,26 +6,26 @@ namespace TagsCloud.Actions; public class CircleCloudAction : IUiAction { - private IImageHolder imageHolder; - private TagCloudPainter painter; + private readonly IImageHolder imageHolder; + private readonly TagCloudPainter painter; private readonly AppSettings settings; public CircleCloudAction( - AppSettings appSettings, IImageHolder imageHolder, TagCloudPainter painter) + AppSettings appSettings, IImageHolder imageHolder, TagCloudPainter painter) { - this.settings = appSettings; + settings = appSettings; this.imageHolder = imageHolder; this.painter = painter; } + public MenuCategory Category => MenuCategory.Types; public string Name => "Круг"; public string Description => ""; + public void Perform() { - if (settings.File == null){ - throw new Exception("сначала загрузи файл"); - } + if (settings.File == null) throw new Exception("сначала загрузи файл"); var size = imageHolder.GetImageSize(); - painter.Paint(new Spiral(new Point(size.Width/2, size.Height/2)), settings.File.FullName); + painter.Paint(new Spiral(new Point(size.Width / 2, size.Height / 2)), settings.File.FullName); } } \ No newline at end of file diff --git a/TagsCloudTests/AnalyzerTests.cs b/TagsCloudTests/AnalyzerTests.cs new file mode 100644 index 000000000..830cc4d72 --- /dev/null +++ b/TagsCloudTests/AnalyzerTests.cs @@ -0,0 +1,99 @@ +using FluentAssertions; +using TagsCloud; +using TagsCloud.WordAnalyzer; + +namespace TagsCloudTests; + +public class AnalyzerTests +{ + private WordAnalyzer sut; + private WordAnalyzerSettings settings; + + [SetUp] + public void SetUp() + { + settings = new WordAnalyzerSettings(); + sut = new WordAnalyzer(settings); + } + + [Test] + public void WordAnalyzer_WhenEmptyCollection_ShouldBeEmpty() + { + sut.GetFrequencyList(new List()).Should().BeEmpty(); + } + + [Test] + public void WordAnalyzer_WhenWithSelectedPartSpeech_ShouldBeOnlySelectedPartSpeech() + { + var words = new List { "он", "плохой", "человек" }; + settings.SelectedSpeeches = new List { PartSpeech.Noun }; + sut.GetFrequencyList(words).Should().BeEquivalentTo(new List { new((string)"человек", (int)1) }); + } + + [Test] + public void WordAnalyzer_WhenWithSomeBoringWords_ShouldBeWithoutBoringWords() + { + var words = new List { "он", "плохой", "человек" }; + settings.BoringWords = new List { "плохой" }; + sut.GetFrequencyList(words).Should().BeEquivalentTo(new List { new((string)"он", (int)1), new((string)"человек", (int)1) }); + } + + [Test] + public void WordAnalyzer_WhenWithExcludedSpeeches_ShouldBeWithoutExcludedSpeeches() + { + var words = new List { "он", "плохой", "человек" }; + settings.ExcludedSpeeches = new List { PartSpeech.Noun }; + sut.GetFrequencyList(words).Should().BeEquivalentTo(new List { new((string)"он", (int)1), new((string)"плохой", (int)1) }); + } + + [Test] + public void WordAnalyzer_WhenDifferentCasesOfWord_ShouldBeWordInInitialForm() + { + var words = new List + { + "человек", + "человека", + "человеку", + "человека", + "человеком", + "человеке", + "читать", + "читая", + "читал", + "читала", + "читало", + "читали" + }; + sut.GetFrequencyList(words).Should().BeEquivalentTo(new List { new((string)"человек", (int)6), new((string)"читать", (int)6) }); + } + + [Test] + public void WordAnalyzer_WhenDifferentCountWords_ShouldBeSortedByDescending() + { + var words = new List + { + "человек", + "человека", + "человеку", + "человека", + "человеком", + "красивый", + "красивая", + "красивое", + "красивые" + }; + sut.GetFrequencyList(words).Should().BeInDescendingOrder(info => info.Count); + } + + [Test] + public void WordAnalyzer_WhenEqualCountWords_ShouldBeSortedByAscendingOrder() + { + var words = new List + { + "человек", + "был", + "красивым" + }; + sut.GetFrequencyList(words).Should().BeInAscendingOrder(info => info.Word); + } +} \ No newline at end of file diff --git a/TagsCloudTests/FileReaderTestData.cs b/TagsCloudTests/FileReaderTestData.cs new file mode 100644 index 000000000..3d63fa670 --- /dev/null +++ b/TagsCloudTests/FileReaderTestData.cs @@ -0,0 +1,23 @@ +using Spire.Doc; + +namespace TagsCloudTests; + +public static class FileReaderTestData +{ + public static IEnumerable ConstructSomeFileTypes => new[] + { + new TestCaseData("TEST.txt", FileFormat.Txt, "They \n call", new[] { "they", "call" }).SetName( + "WhenReadTxtFile"), + new TestCaseData("TEST.doc", FileFormat.Doc, "They \n call", new[] { "they", "call" }).SetName( + "WhenReadDocFile"), + new TestCaseData("TEST.docx", FileFormat.Docx, "ask \n cash \n out", new[] { "ask", "cash", "out" }).SetName( + "WhenReadDocxFile"), + new TestCaseData("litr.txt", FileFormat.Txt, "Straight from .the. hip, cut to the chase\n" + + "Play? no games, say no names", + new[] + { + "straight", "from", "the", "hip", "cut", "to", "the", "chase", "play", "no", "games", "say", "no", + "names" + }).SetName("WhenReadLiteratureText") + }; +} \ No newline at end of file diff --git a/TagsCloudTests/FileReaderTests.cs b/TagsCloudTests/FileReaderTests.cs new file mode 100644 index 000000000..583de7d15 --- /dev/null +++ b/TagsCloudTests/FileReaderTests.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Spire.Doc; +using TagsCloud; + +namespace TagsCloudTests; + +public class FileReaderTests +{ + private Document documentWriter; + private FileReader sut; + public string FileName; + + [SetUp] + public void SetUp() + { + documentWriter = new Document(); + var parsers = new List() { new DocParser(), new TxtParser(), new DocxParser() }; + sut = new FileReader(parsers); + } + + [TearDown] + public void TearDown() + { + documentWriter.Close(); + if (File.Exists(FileName)) + File.Delete(FileName); + } + + [TestCaseSource(typeof(FileReaderTestData), nameof(FileReaderTestData.ConstructSomeFileTypes))] + public void FileReader_GetWordList(string fileName, FileFormat format, string text, string[] expected) + { + FileName = fileName; + File.Create(FileName).Close(); + var paragraph1 = documentWriter.AddSection().AddParagraph(); + paragraph1.Text = text; + documentWriter.SaveToFile(FileName, FileFormat.Txt); + documentWriter.Close(); + var wordList = sut.GetWords(FileName).ToArray()[11..]; + wordList.Should().BeEquivalentTo(expected); + + } + + [Test] + public void FileReader_WhenReadEmptyFile_ShouldBeNotTrow() + { + FileName = "Empty.txt"; + var paragraph1 = documentWriter.AddSection(); + File.Create(FileName).Close(); + documentWriter.SaveToFile(FileName, FileFormat.Txt); + documentWriter.Close(); + var action = () => + { + var wordList = sut.GetWords(FileName); + }; + action.Should().NotThrow(); + } + [Test] + public void FileReader_WhenFileTypeParserDoesNotExist_ShouldBeTrow() + { + FileName = "NotFound.pdf"; + File.Create(FileName).Close(); + documentWriter.SaveToFile(FileName, FileFormat.PDF); + var action = () => + { + var wordList = sut.GetWords(FileName); + }; + action.Should().Throw($"not found parser for {nameof(FileFormat.PDF)}"); + } +} \ No newline at end of file diff --git a/TagsCloudTests/FlowerSpiralTests.cs b/TagsCloudTests/FlowerSpiralTests.cs new file mode 100644 index 000000000..096f5ae60 --- /dev/null +++ b/TagsCloudTests/FlowerSpiralTests.cs @@ -0,0 +1,53 @@ +using System.Drawing; +using FluentAssertions; +using TagsCloud; + +namespace TagsCloudTests; + +public class FlowerSpiralTests +{ + private Point center; + + [SetUp] + public void Setup() + { + center = new Point(10, 10); + } + + private static IEnumerable ConstructorSpiralPoints => new[] + { + new TestCaseData(0, 6, 2.5f, + new Point[] { new(10, 10), new(10, 10),new(10, 10) }) + .SetName("petal Length equals 0"), + new TestCaseData(6, 0, 2.5f, + new Point[] {new(10, 10), new(10, 10),new(10, 10) }) + .SetName("petal Count equals 0"), + new TestCaseData(4, 4, 2.5f, + new Point[] {new(10, 10), new(14, 7),new(15, -8), new(0, -18), new(-15, -6), new(-3, 11) }) + .SetName("AngleStep is positive"), + new TestCaseData(4, 4, -2.5f, + new Point[] {new(10, 10), new(14, 13),new(15, 28), new(0, 38), new(-15, 26), new(-3, 9) }) + .SetName("AngleStep is negative") + }; + + [Test] + public void FlowerSpiral_WhenPetalCountAndPetalLengthLessThat0_ShouldBeThrowException() + { + Action action = () => new FlowerSpiral(center, -1, -1); + action.Should().Throw() + .WithMessage($"petalCount or petalLength must not be less than 0"); + } + + [TestCaseSource(nameof(ConstructorSpiralPoints))] + public void FlowerSpiral_GetNextPoint_CreatePointsWithCustomAngle_ReturnsCorrectPoints(double petalLength, int petalCount, float angleStep, + Point[] expectedPoints) + { + var spiral = new FlowerSpiral(center,petalLength, petalCount, angleStep); + var resultPoints = new Point[expectedPoints.Length]; + for (var i = 0; i < resultPoints.Length; i++) + resultPoints[i] = spiral.GetPoint(); + + resultPoints.Should().BeEquivalentTo(expectedPoints); + } + +} \ No newline at end of file diff --git a/TagsCloudTests/CircularCloudLayouterTests.cs b/TagsCloudTests/LayouterTests.cs similarity index 62% rename from TagsCloudTests/CircularCloudLayouterTests.cs rename to TagsCloudTests/LayouterTests.cs index bf48ec326..d0dbeb69d 100644 --- a/TagsCloudTests/CircularCloudLayouterTests.cs +++ b/TagsCloudTests/LayouterTests.cs @@ -6,40 +6,16 @@ namespace TagsCloudTests; -public class CircularCloudLayouterTests +public class LayouterTests { private ISpiral spiral; - private ICircularCloudLayouter sut; + private ICloudLayouter sut; [SetUp] public void SetUp() { spiral = new Spiral(new Point(10, 10)); - sut = new CircularCloudLayouter(spiral); - } - - [TearDown] - public void TearDown() - { - if (TestContext.CurrentContext.Result.Outcome != ResultState.Failure) return; - var workingDirectory = Environment.CurrentDirectory; - var parentDirectory = Directory.GetParent(workingDirectory)?.Parent; - - var directoryPath = parentDirectory != null - ? Path.Combine(parentDirectory.FullName, "FailedTestImages") - : Path.Combine(workingDirectory, "FailedTestImages"); - - if (!Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - - var imageName = TestContext.CurrentContext.Test.Name; - var imagePath = Path.Combine(directoryPath, $"{imageName}.png"); - - RectanglesVisualizer.GetTagsCloudImage(sut.Rectangles).Save(imagePath, ImageFormat.Png); - - Console.WriteLine($"Tag cloud visualization saved to file {imagePath}"); + sut = new CloudLayouter(spiral); } diff --git a/TagsCloudTests/RectanglesVisualizerTests.cs b/TagsCloudTests/RectanglesVisualizerTests.cs deleted file mode 100644 index daf6550ae..000000000 --- a/TagsCloudTests/RectanglesVisualizerTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Drawing; -using System.Drawing.Imaging; -using FluentAssertions; -using TagsCloud; - -namespace TagsCloudTests; - -public class RectanglesVisualizerTests -{ - private const int MinCoordinate = 0; - private const int MaxCoordinate = 5000; - private static readonly Random Random = new(); - - [Test] - public void GetTagsCloudImage_DrawImageWithoutRectangles_EmptyImage() - { - var rectangles = new List(); - var expected = BitmapToByteArray(new Bitmap(100, 100)); - var result = BitmapToByteArray(RectanglesVisualizer.GetTagsCloudImage(rectangles)); - result.Should().BeEquivalentTo(expected); - } - - [Test] - public void GetTagsCloudImage_DrawSomeRectangles_AllRectanglesInImage() - { - var count = 10; - var rectangles = new List(); - for (var i = 0; i < count; i++) - { - var locate = GetRandomLocation(); - var size = Utils.GetRandomSize(); - var rect = new Rectangle(locate, size); - rectangles.Add(rect); - } - - var image = RectanglesVisualizer.GetTagsCloudImage(rectangles); - CheckImageBorders(rectangles, image).Should().BeTrue(); - } - - private static Point GetRandomLocation() => - new Point(Random.Next(MinCoordinate, MaxCoordinate), Random.Next(MinCoordinate, MaxCoordinate)); - - private static byte[] BitmapToByteArray(Bitmap bitmap) - { - using var stream = new MemoryStream(); - bitmap.Save(stream, ImageFormat.Png); - return stream.ToArray(); - } - - private static bool CheckImageBorders(List rectangles, Bitmap image) => - rectangles.Max(rectangle => rectangle.Bottom) < image.Height && - rectangles.Max(rectangle => rectangle.Right) < image.Width; -} \ No newline at end of file From 7a7bec4687f8ba41ecf249999f2135e0af8f82e4 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Wed, 24 Jan 2024 15:51:06 +0500 Subject: [PATCH 04/10] first refactor --- TagsCloud/Actions/CircleCloudAction.cs | 1 + TagsCloud/Actions/FlowerCloudAction.cs | 9 ++-- TagsCloud/Actions/SaveFileAction.cs | 6 ++- TagsCloud/Actions/TagSettingsAction.cs | 2 + TagsCloud/Actions/UploadFileAction.cs | 4 +- .../Actions/WordAnalyzerSettingsAction.cs | 2 + TagsCloud/{ => Algorithms}/FlowerSpiral.cs | 11 +++-- TagsCloud/{ => Algorithms}/ISpiral.cs | 2 +- TagsCloud/{ => Algorithms}/Spiral.cs | 2 +- TagsCloud/CloudLayouter.cs | 3 +- TagsCloud/CloudLayouterExtension.cs | 4 +- TagsCloud/FileReader.cs | 13 +++--- ...ularCloudLayouter.cs => ICloudLayouter.cs} | 2 +- TagsCloud/ImageSaver.cs | 18 -------- TagsCloud/Parsers/ParserHelper.cs | 2 +- TagsCloud/Program.cs | 18 ++++++++ .../{MainProgram.cs => ProgramConstructor.cs} | 18 +++----- TagsCloud/RectanglesVisualizer.cs | 26 ----------- TagsCloud/Settings/WordAnalyzerSettings.cs | 2 + TagsCloud/Tag.cs | 4 +- TagsCloud/TagCloudPainter.cs | 26 ++++------- TagsCloud/TagCloudVisualizer.cs | 42 ++++++++++++++++++ TagsCloud/TagGenerator.cs | 28 ------------ TagsCloud/Utils.cs | 2 +- TagsCloud/WordAnalyzer.cs | 43 ------------------- TagsCloud/{ => WordAnalyzer}/PartSpeech.cs | 2 +- TagsCloud/WordAnalyzer/WordAnalyzer.cs | 39 +++++++++++++++++ .../{ => WordAnalyzer}/WordAnalyzerHelper.cs | 2 + TagsCloud/{ => WordAnalyzer}/WordInfo.cs | 6 +-- TagsCloudTests/FlowerSpiralTests.cs | 1 + TagsCloudTests/LayouterTests.cs | 1 + TagsCloudTests/SpiralTests.cs | 1 + 32 files changed, 162 insertions(+), 180 deletions(-) rename TagsCloud/{ => Algorithms}/FlowerSpiral.cs (69%) rename TagsCloud/{ => Algorithms}/ISpiral.cs (69%) rename TagsCloud/{ => Algorithms}/Spiral.cs (95%) rename TagsCloud/{ICircularCloudLayouter.cs => ICloudLayouter.cs} (77%) delete mode 100644 TagsCloud/ImageSaver.cs create mode 100644 TagsCloud/Program.cs rename TagsCloud/{MainProgram.cs => ProgramConstructor.cs} (71%) delete mode 100644 TagsCloud/RectanglesVisualizer.cs create mode 100644 TagsCloud/TagCloudVisualizer.cs delete mode 100644 TagsCloud/TagGenerator.cs delete mode 100644 TagsCloud/WordAnalyzer.cs rename TagsCloud/{ => WordAnalyzer}/PartSpeech.cs (80%) create mode 100644 TagsCloud/WordAnalyzer/WordAnalyzer.cs rename TagsCloud/{ => WordAnalyzer}/WordAnalyzerHelper.cs (95%) rename TagsCloud/{ => WordAnalyzer}/WordInfo.cs (73%) diff --git a/TagsCloud/Actions/CircleCloudAction.cs b/TagsCloud/Actions/CircleCloudAction.cs index 44e900e9a..a4172e1d7 100644 --- a/TagsCloud/Actions/CircleCloudAction.cs +++ b/TagsCloud/Actions/CircleCloudAction.cs @@ -1,4 +1,5 @@ using System.Drawing; +using TagsCloud.Algorithms; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; diff --git a/TagsCloud/Actions/FlowerCloudAction.cs b/TagsCloud/Actions/FlowerCloudAction.cs index 7bdd0e93c..8c6c433d8 100644 --- a/TagsCloud/Actions/FlowerCloudAction.cs +++ b/TagsCloud/Actions/FlowerCloudAction.cs @@ -1,4 +1,5 @@ using System.Drawing; +using TagsCloud.Algorithms; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; @@ -17,14 +18,12 @@ public FlowerCloudAction( this.imageHolder = imageHolder; this.painter = painter; } - + public void Perform() { - if (settings.File == null){ - throw new Exception("сначала загрузи файл"); - } + if (settings.File == null) throw new Exception("сначала загрузи файл"); var size = imageHolder.GetImageSize(); - painter.Paint(new FlowerSpiral(new Point(size.Width/2, size.Height/2), 0.5, 4), settings.File.FullName); + painter.Paint(new FlowerSpiral(new Point(size.Width / 2, size.Height / 2), 0.5, 4), settings.File.FullName); } public MenuCategory Category => MenuCategory.Types; diff --git a/TagsCloud/Actions/SaveFileAction.cs b/TagsCloud/Actions/SaveFileAction.cs index 97f680d95..9656a0401 100644 --- a/TagsCloud/Actions/SaveFileAction.cs +++ b/TagsCloud/Actions/SaveFileAction.cs @@ -7,14 +7,16 @@ namespace TagsCloud.Actions; public class SaveFileAction : IUiAction { private readonly IImageHolder imageHolder; - + public SaveFileAction(IImageHolder imageHolder) { this.imageHolder = imageHolder; } + public MenuCategory Category => MenuCategory.File; public string Name => "Сохранить"; public string Description => "Сохранить изображение в файл"; + public void Perform() { var imagesDirectoryPath = Path.GetFullPath("..//..//..//images"); @@ -25,7 +27,7 @@ public void Perform() InitialDirectory = Path.GetFullPath(imagesDirectoryPath), DefaultExt = "png", FileName = "image.png", - Filter = "Изображения (*.png)|*.png" + Filter = "Изображения (*.png)|*.png" }; var res = dialog.ShowDialog(); if (res == DialogResult.OK) diff --git a/TagsCloud/Actions/TagSettingsAction.cs b/TagsCloud/Actions/TagSettingsAction.cs index b31740a03..c47abf512 100644 --- a/TagsCloud/Actions/TagSettingsAction.cs +++ b/TagsCloud/Actions/TagSettingsAction.cs @@ -12,9 +12,11 @@ public TagSettingsAction(TagSettings tag) { this.tag = tag; } + public MenuCategory Category => MenuCategory.Settings; public string Name => "Облако тегов..."; public string Description => ""; + public void Perform() { SettingsForm.For(tag).ShowDialog(); diff --git a/TagsCloud/Actions/UploadFileAction.cs b/TagsCloud/Actions/UploadFileAction.cs index 30147f112..6f35a6ddf 100644 --- a/TagsCloud/Actions/UploadFileAction.cs +++ b/TagsCloud/Actions/UploadFileAction.cs @@ -6,13 +6,11 @@ namespace TagsCloud.Actions; public class UploadFileAction : IUiAction { - private readonly IImageHolder imageHolder; private readonly AppSettings settings; - public UploadFileAction(IImageHolder imageHolder, AppSettings settings) + public UploadFileAction(AppSettings settings) { this.settings = settings; - this.imageHolder = imageHolder; } public MenuCategory Category => MenuCategory.File; public string Name => "Загрузить"; diff --git a/TagsCloud/Actions/WordAnalyzerSettingsAction.cs b/TagsCloud/Actions/WordAnalyzerSettingsAction.cs index 497a6c22f..1aca723bd 100644 --- a/TagsCloud/Actions/WordAnalyzerSettingsAction.cs +++ b/TagsCloud/Actions/WordAnalyzerSettingsAction.cs @@ -11,9 +11,11 @@ public WordAnalyzerSettingsAction(WordAnalyzerSettings wordAnalyzerSettings) { this.wordAnalyzerSettings = wordAnalyzerSettings; } + public MenuCategory Category => MenuCategory.Settings; public string Name => "Анализатор..."; public string Description => ""; + public void Perform() { SettingsForm.For(wordAnalyzerSettings).ShowDialog(); diff --git a/TagsCloud/FlowerSpiral.cs b/TagsCloud/Algorithms/FlowerSpiral.cs similarity index 69% rename from TagsCloud/FlowerSpiral.cs rename to TagsCloud/Algorithms/FlowerSpiral.cs index 9c0608206..ef8e96a90 100644 --- a/TagsCloud/FlowerSpiral.cs +++ b/TagsCloud/Algorithms/FlowerSpiral.cs @@ -1,24 +1,27 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloud.Algorithms; public class FlowerSpiral : ISpiral { private int counter; private readonly float step; private readonly Point center; - private int petalCount; - private double petalLength; + private readonly int petalCount; + private readonly double petalLength; public FlowerSpiral(Point center, double petalLength = 0.25, int petalCount = 1, float step = 0.1f) { this.center = center; this.petalLength = petalLength; this.petalCount = petalCount; + if (this.petalCount < 0 || petalLength < 0) + throw new ArgumentException($"{nameof(petalCount)} or {nameof(petalLength)} must not be less than 0"); if (step == 0) - throw new ArgumentException($"the {nameof(petalCount)} must not be equal to 0"); + throw new ArgumentException($"the {nameof(step)} must not be equal to 0"); this.step = step; } + public Point GetPoint() { var angle = step * counter; diff --git a/TagsCloud/ISpiral.cs b/TagsCloud/Algorithms/ISpiral.cs similarity index 69% rename from TagsCloud/ISpiral.cs rename to TagsCloud/Algorithms/ISpiral.cs index cb2d4cb80..da354b1c0 100644 --- a/TagsCloud/ISpiral.cs +++ b/TagsCloud/Algorithms/ISpiral.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloud.Algorithms; public interface ISpiral { diff --git a/TagsCloud/Spiral.cs b/TagsCloud/Algorithms/Spiral.cs similarity index 95% rename from TagsCloud/Spiral.cs rename to TagsCloud/Algorithms/Spiral.cs index 188cc08e8..f498d6bac 100644 --- a/TagsCloud/Spiral.cs +++ b/TagsCloud/Algorithms/Spiral.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloud.Algorithms; public class Spiral : ISpiral { diff --git a/TagsCloud/CloudLayouter.cs b/TagsCloud/CloudLayouter.cs index 7b7d6b994..5ec57b5bd 100644 --- a/TagsCloud/CloudLayouter.cs +++ b/TagsCloud/CloudLayouter.cs @@ -1,8 +1,9 @@ using System.Drawing; +using TagsCloud.Algorithms; namespace TagsCloud; -public class CloudLayouter : ICircularCloudLayouter +public class CloudLayouter : ICloudLayouter { public List Rectangles { get; } diff --git a/TagsCloud/CloudLayouterExtension.cs b/TagsCloud/CloudLayouterExtension.cs index 9a2361902..f6c2eaaee 100644 --- a/TagsCloud/CloudLayouterExtension.cs +++ b/TagsCloud/CloudLayouterExtension.cs @@ -2,9 +2,9 @@ namespace TagsCloud; -public static class CloudLayouterExtension +public static class CloudLayouterExtension { - public static List GetRectanglesLocation(this ICircularCloudLayouter layouter) + public static List GetRectanglesLocation(this ICloudLayouter layouter) { return layouter.Rectangles.Select(rectangle => rectangle.Location).ToList(); } diff --git a/TagsCloud/FileReader.cs b/TagsCloud/FileReader.cs index 61279317d..6ee09e568 100644 --- a/TagsCloud/FileReader.cs +++ b/TagsCloud/FileReader.cs @@ -1,11 +1,8 @@ -using System.IO; -using Spire.Doc; - namespace TagsCloud; public class FileReader { - private readonly Dictionary parsers; + private readonly Dictionary parsers; public FileReader(IEnumerable parsers) { @@ -14,11 +11,13 @@ public FileReader(IEnumerable parsers) public IEnumerable GetWords(string filePath) { + if (!File.Exists(filePath)) + throw new ArgumentException($"Error: File not found at the specified path '{filePath}'."); + var fileType = Path.GetExtension(filePath).Trim('.').ToLower(); - if (!parsers.ContainsKey(fileType)){ + if (!parsers.ContainsKey(fileType)) throw new ArgumentException($"not found parser for {fileType}"); - } - + return parsers[fileType].GetWordList(filePath); } } \ No newline at end of file diff --git a/TagsCloud/ICircularCloudLayouter.cs b/TagsCloud/ICloudLayouter.cs similarity index 77% rename from TagsCloud/ICircularCloudLayouter.cs rename to TagsCloud/ICloudLayouter.cs index 103619a6b..ea4bfcd65 100644 --- a/TagsCloud/ICircularCloudLayouter.cs +++ b/TagsCloud/ICloudLayouter.cs @@ -2,7 +2,7 @@ namespace TagsCloud; -public interface ICircularCloudLayouter +public interface ICloudLayouter { List Rectangles { get; } Rectangle PutNextRectangle(Size rectangleSize); diff --git a/TagsCloud/ImageSaver.cs b/TagsCloud/ImageSaver.cs deleted file mode 100644 index a6fddc3b4..000000000 --- a/TagsCloud/ImageSaver.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Drawing.Imaging; - -namespace TagsCloud; - -public static class ImageSaver -{ - public static void SaveImage(string workingDirectory, List tags, string imageName) - { - /* var imagesDirectoryPath = Path.Combine(workingDirectory, "images"); - - if (!Directory.Exists(imagesDirectoryPath)) Directory.CreateDirectory(imagesDirectoryPath); - - var imagePath = Path.Combine(imagesDirectoryPath, $"{imageName}.png"); - - using var image = RectanglesVisualizer.RenderCloudImage(tags); - image.Save(imagePath, ImageFormat.Png); -*/ } -} \ No newline at end of file diff --git a/TagsCloud/Parsers/ParserHelper.cs b/TagsCloud/Parsers/ParserHelper.cs index de09d6a63..62c62f83a 100644 --- a/TagsCloud/Parsers/ParserHelper.cs +++ b/TagsCloud/Parsers/ParserHelper.cs @@ -14,7 +14,7 @@ public static class ParserHelper { foreach (var word in SelectAllWordsRegex.Matches(section.Paragraphs[i].Text)) { - yield return word.ToString(); + yield return word.ToString().Trim().ToLower(); } } } diff --git a/TagsCloud/Program.cs b/TagsCloud/Program.cs new file mode 100644 index 000000000..5bdc447d0 --- /dev/null +++ b/TagsCloud/Program.cs @@ -0,0 +1,18 @@ +using System.Windows.Forms; +using Autofac; + +namespace TagsCloud; + +public class Program +{ + [STAThread] + public static void Main(string[] args) + { + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + var program = ProgramConstructor.ConstructProgram(); + var cloudForm = program.Resolve(); + Application.Run(cloudForm); + } +} \ No newline at end of file diff --git a/TagsCloud/MainProgram.cs b/TagsCloud/ProgramConstructor.cs similarity index 71% rename from TagsCloud/MainProgram.cs rename to TagsCloud/ProgramConstructor.cs index 5f44b72b6..2b754a1e3 100644 --- a/TagsCloud/MainProgram.cs +++ b/TagsCloud/ProgramConstructor.cs @@ -1,22 +1,15 @@ - using System.Reflection; -using System.Windows.Forms; using Autofac; -using TagsCloud.Actions; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; using TagsCloud.Settings; namespace TagsCloud; -public class MainProgram +public static class ProgramConstructor { - [STAThread] - public static void Main(string[] args) + public static IContainer ConstructProgram() { - Application.SetHighDpiMode(HighDpiMode.SystemAware); - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .Where(t => typeof(IUiAction).IsAssignableFrom(t)) @@ -27,12 +20,13 @@ public static void Main(string[] args) containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType(); containerBuilder.RegisterType().AsSelf().SingleInstance(); - containerBuilder.RegisterType().As().SingleInstance(); + containerBuilder.RegisterType().As() + .SingleInstance(); containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.RegisterType(); containerBuilder.RegisterType().AsSelf().SingleInstance(); - var cloudForm = containerBuilder.Build().Resolve(); - Application.Run(cloudForm); + return containerBuilder.Build(); } } \ No newline at end of file diff --git a/TagsCloud/RectanglesVisualizer.cs b/TagsCloud/RectanglesVisualizer.cs deleted file mode 100644 index 9464c7ed7..000000000 --- a/TagsCloud/RectanglesVisualizer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Drawing; -using TagsCloud.Infrastructure; - -namespace TagsCloud; - -public static class RectanglesVisualizer -{ - - public static void RenderCloudImage(List tags, Graphics graphics, IImageHolder imageHolder) - { - var sizeImage = imageHolder.GetImageSize(); - var background = new SolidBrush(Color.Black); - graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); - DrawTagsCloud(tags, graphics); - } - - private static void DrawTagsCloud(List tags, Graphics graphics) - { - foreach (var tag in tags) - { - var brush = new SolidBrush(tag.Color); - graphics.DrawString(tag.Word, tag.Font, brush, tag.Rectangle.Location); - - } - } -} \ No newline at end of file diff --git a/TagsCloud/Settings/WordAnalyzerSettings.cs b/TagsCloud/Settings/WordAnalyzerSettings.cs index f8bfcb054..fe2150aa6 100644 --- a/TagsCloud/Settings/WordAnalyzerSettings.cs +++ b/TagsCloud/Settings/WordAnalyzerSettings.cs @@ -1,3 +1,5 @@ +using TagsCloud.WordAnalyzer; + namespace TagsCloud; public class WordAnalyzerSettings diff --git a/TagsCloud/Tag.cs b/TagsCloud/Tag.cs index 5c8332e3a..1f19da299 100644 --- a/TagsCloud/Tag.cs +++ b/TagsCloud/Tag.cs @@ -8,8 +8,8 @@ public class Tag public readonly string Word; public Rectangle Rectangle; public Color Color; - - public Tag(Font font,string word, Rectangle rectangle, Color color) + + public Tag(Font font, string word, Rectangle rectangle, Color color) { Color = color; Font = font; diff --git a/TagsCloud/TagCloudPainter.cs b/TagsCloud/TagCloudPainter.cs index 0663c3006..2282e55d7 100644 --- a/TagsCloud/TagCloudPainter.cs +++ b/TagsCloud/TagCloudPainter.cs @@ -1,8 +1,6 @@ -using Castle.Core.Internal; -using DeepMorphy; +using TagsCloud.Algorithms; using TagsCloud.Infrastructure; using TagsCloud.Settings; - namespace TagsCloud; public class TagCloudPainter @@ -10,30 +8,24 @@ public class TagCloudPainter private readonly IImageHolder imageHolder; private readonly TagSettings tagSettings; - private readonly WordAnalyzerSettings wordAnalyzerSettings; + private readonly WordAnalyzer.WordAnalyzer wordAnalyzer; private readonly FileReader fileReader; - public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAnalyzerSettings wordAnalyzerSettings, + public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAnalyzer.WordAnalyzer wordAnalyzer, FileReader reader) { this.imageHolder = imageHolder; this.tagSettings = tagSettings; - this.wordAnalyzerSettings = wordAnalyzerSettings; + this.wordAnalyzer = wordAnalyzer; fileReader = reader; } - public void Paint(ISpiral spiral, string path) { - using (var graphics = imageHolder.StartDrawing()) - { - var wordAnalyzer = new WordAnalyzer(wordAnalyzerSettings); - var layout = new CloudLayouter(spiral); - var wordList = fileReader.GetWords(path); - var parsedList = wordAnalyzer.GetFrequencyList(wordList); - var tagGenerator = new TagGenerator(layout, tagSettings); - RectanglesVisualizer.RenderCloudImage(tagGenerator.GetTagsList(parsedList).ToList(), graphics, imageHolder); - imageHolder.UpdateUi(); - } + using var graphics = imageHolder.StartDrawing(); + var layout = new CloudLayouter(spiral); + var wordList = fileReader.GetWords(path); + var parsedList = wordAnalyzer.GetFrequencyList(wordList); + new TagCloudVisualizer(graphics, tagSettings, layout).RenderCloudImage(parsedList, imageHolder.GetImageSize()); imageHolder.UpdateUi(); } } \ No newline at end of file diff --git a/TagsCloud/TagCloudVisualizer.cs b/TagsCloud/TagCloudVisualizer.cs new file mode 100644 index 000000000..9f82be46f --- /dev/null +++ b/TagsCloud/TagCloudVisualizer.cs @@ -0,0 +1,42 @@ +using System.Drawing; +using System.Windows.Forms; +using TagsCloud.Settings; +using TagsCloud.WordAnalyzer; + +namespace TagsCloud; + +public class TagCloudVisualizer +{ + private Graphics graphics; + private readonly TagSettings tagSettings; + private ICloudLayouter cloudLayouter; + public TagCloudVisualizer(Graphics graphics, TagSettings tagSettings, ICloudLayouter cloudLayouter) + { + this.graphics = graphics; + this.tagSettings = tagSettings; + this.cloudLayouter = cloudLayouter; + } + public void RenderCloudImage(IEnumerable words, Size sizeImage) + { + var background = new SolidBrush(Color.Black); + graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); + DrawTagsCloud(words); + } + private Tag GetTag(WordInfo wordInfo) + { + var font = new Font(tagSettings.FontFamily, wordInfo.Count * tagSettings.Size); + var textSize = TextRenderer.MeasureText(wordInfo.Word, font); + var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); + return new Tag(font, wordInfo.Word, textRectangle, tagSettings.Color); + } + + private void DrawTagsCloud(IEnumerablewords) + { + foreach (var word in words) + { + var tag = GetTag(word); + var brush = new SolidBrush(tag.Color); + graphics.DrawString(tag.Word, tag.Font, brush, tag.Rectangle.Location); + } + } +} \ No newline at end of file diff --git a/TagsCloud/TagGenerator.cs b/TagsCloud/TagGenerator.cs deleted file mode 100644 index 8b6cd50d6..000000000 --- a/TagsCloud/TagGenerator.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Drawing; -using System.Windows.Forms; -using TagsCloud.Settings; - -namespace TagsCloud; - -public class TagGenerator -{ - private readonly ICircularCloudLayouter cloudLayouter; - private readonly TagSettings settings; - public TagGenerator(ICircularCloudLayouter cloudLayouter, TagSettings settings) - { - this.cloudLayouter = cloudLayouter; - this.settings = settings; - - } - - public IEnumerable GetTagsList(IEnumerable wordInfos) - { - foreach (var info in wordInfos) - { - var font = new Font(settings.FontFamily, info.Count * settings.Size); - var textSize = TextRenderer.MeasureText(info.Word, font); - var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); - yield return new Tag(font, info.Word, textRectangle, settings.Color); - } - } -} \ No newline at end of file diff --git a/TagsCloud/Utils.cs b/TagsCloud/Utils.cs index 6a17005f2..00d533bad 100644 --- a/TagsCloud/Utils.cs +++ b/TagsCloud/Utils.cs @@ -2,7 +2,7 @@ namespace TagsCloud; -public class Utils +public static class Utils { private static readonly Random Random = new(); private const int MinSize = 1; diff --git a/TagsCloud/WordAnalyzer.cs b/TagsCloud/WordAnalyzer.cs deleted file mode 100644 index aff15459b..000000000 --- a/TagsCloud/WordAnalyzer.cs +++ /dev/null @@ -1,43 +0,0 @@ -using DeepMorphy; - -namespace TagsCloud; - -public class WordAnalyzer -{ - private readonly List ExcludedSpeeches; - private readonly List SelectedSpeeches; - private readonly List BoringWords; - - public WordAnalyzer(WordAnalyzerSettings settings) - { - ExcludedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(settings.ExcludedSpeeches); - SelectedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(settings.SelectedSpeeches); - BoringWords = settings.BoringWords; - - } - - private IEnumerable GetFilteredWords(IEnumerable words) - { - var morphAnalyzer = new MorphAnalyzer(true); - var morphInfos = morphAnalyzer.Parse(words); - return morphInfos.Where(morphInfo => !BoringWords.Contains(morphInfo.BestTag.Lemma) && morphInfo.Tags.All(tag => - !ExcludedSpeeches.Contains(tag["чр"]) && - (SelectedSpeeches.Count == 0 || - SelectedSpeeches - .Contains(tag["чр"])))) - .Select(info => info.BestTag.HasLemma ? info.BestTag.Lemma : info.Text); - } - - public IEnumerable GetFrequencyList(IEnumerable words) - { - var parsedWords = new Dictionary(); - foreach (var word in GetFilteredWords(words)) - { - parsedWords.TryAdd(word, 0); - parsedWords[word]++; - } - - return parsedWords.Select(x => WordInfo.Create(x.Key, x.Value)).OrderByDescending(info => info.Count) - .ThenByDescending(info => info.Word); - } -} \ No newline at end of file diff --git a/TagsCloud/PartSpeech.cs b/TagsCloud/WordAnalyzer/PartSpeech.cs similarity index 80% rename from TagsCloud/PartSpeech.cs rename to TagsCloud/WordAnalyzer/PartSpeech.cs index c6cc36f0f..4d907fecc 100644 --- a/TagsCloud/PartSpeech.cs +++ b/TagsCloud/WordAnalyzer/PartSpeech.cs @@ -1,4 +1,4 @@ -namespace TagsCloud; +namespace TagsCloud.WordAnalyzer; public enum PartSpeech { diff --git a/TagsCloud/WordAnalyzer/WordAnalyzer.cs b/TagsCloud/WordAnalyzer/WordAnalyzer.cs new file mode 100644 index 000000000..fbf808aac --- /dev/null +++ b/TagsCloud/WordAnalyzer/WordAnalyzer.cs @@ -0,0 +1,39 @@ +using DeepMorphy; + +namespace TagsCloud.WordAnalyzer; + +public class WordAnalyzer +{ + private readonly WordAnalyzerSettings Settings; + + public WordAnalyzer(WordAnalyzerSettings settings) + { + Settings = settings; + } + + private IEnumerable GetFilteredWords(IEnumerable words) + { + var excludedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(Settings.ExcludedSpeeches); + var selectedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(Settings.SelectedSpeeches); + var morphAnalyzer = new MorphAnalyzer(true); + var morphInfos = morphAnalyzer.Parse(words); + return morphInfos.Where(morphInfo => !Settings.BoringWords.Contains(morphInfo.BestTag.Lemma) && + !excludedSpeeches.Contains(morphInfo.BestTag["чр"]) && + (selectedSpeeches.Count == 0 || + selectedSpeeches.Contains(morphInfo.BestTag["чр"]))) + .Select(info => info.BestTag.HasLemma ? info.BestTag.Lemma : info.Text); + } + + public IEnumerable GetFrequencyList(IEnumerable words) + { + var parsedWords = new Dictionary(); + foreach (var word in GetFilteredWords(words)) + { + parsedWords.TryAdd(word, 0); + parsedWords[word]++; + } + + return parsedWords.Select(x => new WordInfo(x.Key, x.Value)).OrderByDescending(info => info.Count) + .ThenBy(info => info.Word); + } +} \ No newline at end of file diff --git a/TagsCloud/WordAnalyzerHelper.cs b/TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs similarity index 95% rename from TagsCloud/WordAnalyzerHelper.cs rename to TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs index 9d838ef43..37811874f 100644 --- a/TagsCloud/WordAnalyzerHelper.cs +++ b/TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs @@ -1,3 +1,5 @@ +using TagsCloud.WordAnalyzer; + namespace TagsCloud; public static class WordAnalyzerHelper diff --git a/TagsCloud/WordInfo.cs b/TagsCloud/WordAnalyzer/WordInfo.cs similarity index 73% rename from TagsCloud/WordInfo.cs rename to TagsCloud/WordAnalyzer/WordInfo.cs index bd23aa16e..cbefdaefc 100644 --- a/TagsCloud/WordInfo.cs +++ b/TagsCloud/WordAnalyzer/WordInfo.cs @@ -1,13 +1,11 @@ -using System.Windows.Forms; - -namespace TagsCloud; +namespace TagsCloud.WordAnalyzer; public class WordInfo { public readonly int Count; public readonly string Word; - private WordInfo(string word, int count) + public WordInfo(string word, int count) { Word = word; Count = count; diff --git a/TagsCloudTests/FlowerSpiralTests.cs b/TagsCloudTests/FlowerSpiralTests.cs index 096f5ae60..a9c67c43a 100644 --- a/TagsCloudTests/FlowerSpiralTests.cs +++ b/TagsCloudTests/FlowerSpiralTests.cs @@ -1,6 +1,7 @@ using System.Drawing; using FluentAssertions; using TagsCloud; +using TagsCloud.Algorithms; namespace TagsCloudTests; diff --git a/TagsCloudTests/LayouterTests.cs b/TagsCloudTests/LayouterTests.cs index d0dbeb69d..a6fef76ce 100644 --- a/TagsCloudTests/LayouterTests.cs +++ b/TagsCloudTests/LayouterTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using NUnit.Framework.Interfaces; using TagsCloud; +using TagsCloud.Algorithms; namespace TagsCloudTests; diff --git a/TagsCloudTests/SpiralTests.cs b/TagsCloudTests/SpiralTests.cs index 53b3c50b4..ccdab60eb 100644 --- a/TagsCloudTests/SpiralTests.cs +++ b/TagsCloudTests/SpiralTests.cs @@ -1,6 +1,7 @@ using System.Drawing; using FluentAssertions; using TagsCloud; +using TagsCloud.Algorithms; namespace TagsCloudTests; From dcc0bcf71ff2748da92e1f18dd9f729ff8c8c5e4 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Wed, 24 Jan 2024 16:12:45 +0500 Subject: [PATCH 05/10] put the files in folders --- TagsCloud/Actions/CircleCloudAction.cs | 3 ++- TagsCloud/Actions/FlowerCloudAction.cs | 3 ++- TagsCloud/{ => CloudLayouter}/CloudLayouter.cs | 3 +-- TagsCloud/{ => CloudLayouter}/CloudLayouterExtension.cs | 2 +- TagsCloud/{Algorithms => CloudLayouter}/FlowerSpiral.cs | 2 +- TagsCloud/{ => CloudLayouter}/ICloudLayouter.cs | 2 +- TagsCloud/{Algorithms => CloudLayouter}/ISpiral.cs | 2 +- TagsCloud/{Algorithms => CloudLayouter}/Spiral.cs | 2 +- TagsCloud/{ => CloudPainter}/Tag.cs | 2 +- TagsCloud/{ => CloudPainter}/TagCloudPainter.cs | 7 ++++--- TagsCloud/{ => CloudPainter}/TagCloudVisualizer.cs | 3 ++- TagsCloud/{Parsers => FileReader}/DocParser.cs | 0 TagsCloud/{Parsers => FileReader}/DocxParser.cs | 0 TagsCloud/{ => FileReader}/FileReader.cs | 0 TagsCloud/{Parsers => FileReader}/IParser.cs | 0 TagsCloud/{Parsers => FileReader}/ParserHelper.cs | 0 TagsCloud/{Parsers => FileReader}/TxtParser.cs | 0 TagsCloud/ProgramConstructor.cs | 1 + TagsCloud/TagsCloud.csproj.DotSettings | 1 + TagsCloud/WordAnalyzer/WordAnalyzer.cs | 2 +- TagsCloudTests/FlowerSpiralTests.cs | 2 +- TagsCloudTests/LayouterTests.cs | 2 +- TagsCloudTests/SpiralTests.cs | 2 +- {TagsCloud => TagsCloudTests}/Utils.cs | 9 +-------- 24 files changed, 24 insertions(+), 26 deletions(-) rename TagsCloud/{ => CloudLayouter}/CloudLayouter.cs (96%) rename TagsCloud/{ => CloudLayouter}/CloudLayouterExtension.cs (87%) rename TagsCloud/{Algorithms => CloudLayouter}/FlowerSpiral.cs (97%) rename TagsCloud/{ => CloudLayouter}/ICloudLayouter.cs (81%) rename TagsCloud/{Algorithms => CloudLayouter}/ISpiral.cs (67%) rename TagsCloud/{Algorithms => CloudLayouter}/Spiral.cs (95%) rename TagsCloud/{ => CloudPainter}/Tag.cs (91%) rename TagsCloud/{ => CloudPainter}/TagCloudPainter.cs (88%) rename TagsCloud/{ => CloudPainter}/TagCloudVisualizer.cs (95%) rename TagsCloud/{Parsers => FileReader}/DocParser.cs (100%) rename TagsCloud/{Parsers => FileReader}/DocxParser.cs (100%) rename TagsCloud/{ => FileReader}/FileReader.cs (100%) rename TagsCloud/{Parsers => FileReader}/IParser.cs (100%) rename TagsCloud/{Parsers => FileReader}/ParserHelper.cs (100%) rename TagsCloud/{Parsers => FileReader}/TxtParser.cs (100%) rename {TagsCloud => TagsCloudTests}/Utils.cs (62%) diff --git a/TagsCloud/Actions/CircleCloudAction.cs b/TagsCloud/Actions/CircleCloudAction.cs index a4172e1d7..5d82a8bd2 100644 --- a/TagsCloud/Actions/CircleCloudAction.cs +++ b/TagsCloud/Actions/CircleCloudAction.cs @@ -1,5 +1,6 @@ using System.Drawing; -using TagsCloud.Algorithms; +using TagsCloud.CloudLayouter; +using TagsCloud.CloudPainter; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; diff --git a/TagsCloud/Actions/FlowerCloudAction.cs b/TagsCloud/Actions/FlowerCloudAction.cs index 8c6c433d8..164f6d195 100644 --- a/TagsCloud/Actions/FlowerCloudAction.cs +++ b/TagsCloud/Actions/FlowerCloudAction.cs @@ -1,5 +1,6 @@ using System.Drawing; -using TagsCloud.Algorithms; +using TagsCloud.CloudLayouter; +using TagsCloud.CloudPainter; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; diff --git a/TagsCloud/CloudLayouter.cs b/TagsCloud/CloudLayouter/CloudLayouter.cs similarity index 96% rename from TagsCloud/CloudLayouter.cs rename to TagsCloud/CloudLayouter/CloudLayouter.cs index 5ec57b5bd..88be5ec36 100644 --- a/TagsCloud/CloudLayouter.cs +++ b/TagsCloud/CloudLayouter/CloudLayouter.cs @@ -1,7 +1,6 @@ using System.Drawing; -using TagsCloud.Algorithms; -namespace TagsCloud; +namespace TagsCloud.CloudLayouter; public class CloudLayouter : ICloudLayouter { diff --git a/TagsCloud/CloudLayouterExtension.cs b/TagsCloud/CloudLayouter/CloudLayouterExtension.cs similarity index 87% rename from TagsCloud/CloudLayouterExtension.cs rename to TagsCloud/CloudLayouter/CloudLayouterExtension.cs index f6c2eaaee..933aaa0ae 100644 --- a/TagsCloud/CloudLayouterExtension.cs +++ b/TagsCloud/CloudLayouter/CloudLayouterExtension.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloud.CloudLayouter; public static class CloudLayouterExtension { diff --git a/TagsCloud/Algorithms/FlowerSpiral.cs b/TagsCloud/CloudLayouter/FlowerSpiral.cs similarity index 97% rename from TagsCloud/Algorithms/FlowerSpiral.cs rename to TagsCloud/CloudLayouter/FlowerSpiral.cs index ef8e96a90..8abcf4bcb 100644 --- a/TagsCloud/Algorithms/FlowerSpiral.cs +++ b/TagsCloud/CloudLayouter/FlowerSpiral.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud.Algorithms; +namespace TagsCloud.CloudLayouter; public class FlowerSpiral : ISpiral { diff --git a/TagsCloud/ICloudLayouter.cs b/TagsCloud/CloudLayouter/ICloudLayouter.cs similarity index 81% rename from TagsCloud/ICloudLayouter.cs rename to TagsCloud/CloudLayouter/ICloudLayouter.cs index ea4bfcd65..a6476ab0d 100644 --- a/TagsCloud/ICloudLayouter.cs +++ b/TagsCloud/CloudLayouter/ICloudLayouter.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloud.CloudLayouter; public interface ICloudLayouter { diff --git a/TagsCloud/Algorithms/ISpiral.cs b/TagsCloud/CloudLayouter/ISpiral.cs similarity index 67% rename from TagsCloud/Algorithms/ISpiral.cs rename to TagsCloud/CloudLayouter/ISpiral.cs index da354b1c0..f425a8dc3 100644 --- a/TagsCloud/Algorithms/ISpiral.cs +++ b/TagsCloud/CloudLayouter/ISpiral.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud.Algorithms; +namespace TagsCloud.CloudLayouter; public interface ISpiral { diff --git a/TagsCloud/Algorithms/Spiral.cs b/TagsCloud/CloudLayouter/Spiral.cs similarity index 95% rename from TagsCloud/Algorithms/Spiral.cs rename to TagsCloud/CloudLayouter/Spiral.cs index f498d6bac..02351dff2 100644 --- a/TagsCloud/Algorithms/Spiral.cs +++ b/TagsCloud/CloudLayouter/Spiral.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud.Algorithms; +namespace TagsCloud.CloudLayouter; public class Spiral : ISpiral { diff --git a/TagsCloud/Tag.cs b/TagsCloud/CloudPainter/Tag.cs similarity index 91% rename from TagsCloud/Tag.cs rename to TagsCloud/CloudPainter/Tag.cs index 1f19da299..19d5e5b1f 100644 --- a/TagsCloud/Tag.cs +++ b/TagsCloud/CloudPainter/Tag.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloud.CloudPainter; public class Tag { diff --git a/TagsCloud/TagCloudPainter.cs b/TagsCloud/CloudPainter/TagCloudPainter.cs similarity index 88% rename from TagsCloud/TagCloudPainter.cs rename to TagsCloud/CloudPainter/TagCloudPainter.cs index 2282e55d7..e4add4392 100644 --- a/TagsCloud/TagCloudPainter.cs +++ b/TagsCloud/CloudPainter/TagCloudPainter.cs @@ -1,7 +1,8 @@ -using TagsCloud.Algorithms; +using TagsCloud.CloudLayouter; using TagsCloud.Infrastructure; using TagsCloud.Settings; -namespace TagsCloud; + +namespace TagsCloud.CloudPainter; public class TagCloudPainter { @@ -22,7 +23,7 @@ public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAn public void Paint(ISpiral spiral, string path) { using var graphics = imageHolder.StartDrawing(); - var layout = new CloudLayouter(spiral); + var layout = new CloudLayouter.CloudLayouter(spiral); var wordList = fileReader.GetWords(path); var parsedList = wordAnalyzer.GetFrequencyList(wordList); new TagCloudVisualizer(graphics, tagSettings, layout).RenderCloudImage(parsedList, imageHolder.GetImageSize()); diff --git a/TagsCloud/TagCloudVisualizer.cs b/TagsCloud/CloudPainter/TagCloudVisualizer.cs similarity index 95% rename from TagsCloud/TagCloudVisualizer.cs rename to TagsCloud/CloudPainter/TagCloudVisualizer.cs index 9f82be46f..249d154e8 100644 --- a/TagsCloud/TagCloudVisualizer.cs +++ b/TagsCloud/CloudPainter/TagCloudVisualizer.cs @@ -1,9 +1,10 @@ using System.Drawing; using System.Windows.Forms; +using TagsCloud.CloudLayouter; using TagsCloud.Settings; using TagsCloud.WordAnalyzer; -namespace TagsCloud; +namespace TagsCloud.CloudPainter; public class TagCloudVisualizer { diff --git a/TagsCloud/Parsers/DocParser.cs b/TagsCloud/FileReader/DocParser.cs similarity index 100% rename from TagsCloud/Parsers/DocParser.cs rename to TagsCloud/FileReader/DocParser.cs diff --git a/TagsCloud/Parsers/DocxParser.cs b/TagsCloud/FileReader/DocxParser.cs similarity index 100% rename from TagsCloud/Parsers/DocxParser.cs rename to TagsCloud/FileReader/DocxParser.cs diff --git a/TagsCloud/FileReader.cs b/TagsCloud/FileReader/FileReader.cs similarity index 100% rename from TagsCloud/FileReader.cs rename to TagsCloud/FileReader/FileReader.cs diff --git a/TagsCloud/Parsers/IParser.cs b/TagsCloud/FileReader/IParser.cs similarity index 100% rename from TagsCloud/Parsers/IParser.cs rename to TagsCloud/FileReader/IParser.cs diff --git a/TagsCloud/Parsers/ParserHelper.cs b/TagsCloud/FileReader/ParserHelper.cs similarity index 100% rename from TagsCloud/Parsers/ParserHelper.cs rename to TagsCloud/FileReader/ParserHelper.cs diff --git a/TagsCloud/Parsers/TxtParser.cs b/TagsCloud/FileReader/TxtParser.cs similarity index 100% rename from TagsCloud/Parsers/TxtParser.cs rename to TagsCloud/FileReader/TxtParser.cs diff --git a/TagsCloud/ProgramConstructor.cs b/TagsCloud/ProgramConstructor.cs index 2b754a1e3..8af15d7d8 100644 --- a/TagsCloud/ProgramConstructor.cs +++ b/TagsCloud/ProgramConstructor.cs @@ -1,5 +1,6 @@ using System.Reflection; using Autofac; +using TagsCloud.CloudPainter; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; using TagsCloud.Settings; diff --git a/TagsCloud/TagsCloud.csproj.DotSettings b/TagsCloud/TagsCloud.csproj.DotSettings index 7eefcc1b9..ca5994cdb 100644 --- a/TagsCloud/TagsCloud.csproj.DotSettings +++ b/TagsCloud/TagsCloud.csproj.DotSettings @@ -1,2 +1,3 @@  + True True \ No newline at end of file diff --git a/TagsCloud/WordAnalyzer/WordAnalyzer.cs b/TagsCloud/WordAnalyzer/WordAnalyzer.cs index fbf808aac..1e2f30cf7 100644 --- a/TagsCloud/WordAnalyzer/WordAnalyzer.cs +++ b/TagsCloud/WordAnalyzer/WordAnalyzer.cs @@ -17,7 +17,7 @@ private IEnumerable GetFilteredWords(IEnumerable words) var selectedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(Settings.SelectedSpeeches); var morphAnalyzer = new MorphAnalyzer(true); var morphInfos = morphAnalyzer.Parse(words); - return morphInfos.Where(morphInfo => !Settings.BoringWords.Contains(morphInfo.BestTag.Lemma) && + return morphInfos.Where(morphInfo => !Settings.BoringWords.Any(item => item.Equals(morphInfo.BestTag.Lemma, StringComparison.OrdinalIgnoreCase)) && !excludedSpeeches.Contains(morphInfo.BestTag["чр"]) && (selectedSpeeches.Count == 0 || selectedSpeeches.Contains(morphInfo.BestTag["чр"]))) diff --git a/TagsCloudTests/FlowerSpiralTests.cs b/TagsCloudTests/FlowerSpiralTests.cs index a9c67c43a..500e2d23c 100644 --- a/TagsCloudTests/FlowerSpiralTests.cs +++ b/TagsCloudTests/FlowerSpiralTests.cs @@ -1,7 +1,7 @@ using System.Drawing; using FluentAssertions; using TagsCloud; -using TagsCloud.Algorithms; +using TagsCloud.CloudLayouter; namespace TagsCloudTests; diff --git a/TagsCloudTests/LayouterTests.cs b/TagsCloudTests/LayouterTests.cs index a6fef76ce..acf6ec5db 100644 --- a/TagsCloudTests/LayouterTests.cs +++ b/TagsCloudTests/LayouterTests.cs @@ -3,7 +3,7 @@ using FluentAssertions; using NUnit.Framework.Interfaces; using TagsCloud; -using TagsCloud.Algorithms; +using TagsCloud.CloudLayouter; namespace TagsCloudTests; diff --git a/TagsCloudTests/SpiralTests.cs b/TagsCloudTests/SpiralTests.cs index ccdab60eb..ce7561afc 100644 --- a/TagsCloudTests/SpiralTests.cs +++ b/TagsCloudTests/SpiralTests.cs @@ -1,7 +1,7 @@ using System.Drawing; using FluentAssertions; using TagsCloud; -using TagsCloud.Algorithms; +using TagsCloud.CloudLayouter; namespace TagsCloudTests; diff --git a/TagsCloud/Utils.cs b/TagsCloudTests/Utils.cs similarity index 62% rename from TagsCloud/Utils.cs rename to TagsCloudTests/Utils.cs index 00d533bad..6e0b9ff17 100644 --- a/TagsCloud/Utils.cs +++ b/TagsCloudTests/Utils.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud; +namespace TagsCloudTests; public static class Utils { @@ -8,13 +8,6 @@ public static class Utils private const int MinSize = 1; private const int MaxSize = 50; - - public static Color GetRandomColor() => Color.FromArgb(GetShade(), GetShade(), GetShade()); - - - private static int GetShade() => Random.Next(0, 256); - - public static Size GetRandomSize() => new Size(Random.Next(MinSize, MaxSize), Random.Next(MinSize, MaxSize)); } \ No newline at end of file From b20751a0621fae218f1142df80b090f1bdcd389c Mon Sep 17 00:00:00 2001 From: PavelUd Date: Wed, 24 Jan 2024 16:33:06 +0500 Subject: [PATCH 06/10] deleted the same class --- TagsCloud/CloudPainter/TagCloudPainter.cs | 29 +++++++++++-- TagsCloud/CloudPainter/TagCloudVisualizer.cs | 43 -------------------- 2 files changed, 26 insertions(+), 46 deletions(-) delete mode 100644 TagsCloud/CloudPainter/TagCloudVisualizer.cs diff --git a/TagsCloud/CloudPainter/TagCloudPainter.cs b/TagsCloud/CloudPainter/TagCloudPainter.cs index e4add4392..d0b5692d9 100644 --- a/TagsCloud/CloudPainter/TagCloudPainter.cs +++ b/TagsCloud/CloudPainter/TagCloudPainter.cs @@ -1,6 +1,9 @@ +using System.Drawing; +using System.Windows.Forms; using TagsCloud.CloudLayouter; using TagsCloud.Infrastructure; using TagsCloud.Settings; +using TagsCloud.WordAnalyzer; namespace TagsCloud.CloudPainter; @@ -23,10 +26,30 @@ public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAn public void Paint(ISpiral spiral, string path) { using var graphics = imageHolder.StartDrawing(); - var layout = new CloudLayouter.CloudLayouter(spiral); + var sizeImage = imageHolder.GetImageSize(); + var cloudLayouter = new CloudLayouter.CloudLayouter(spiral); var wordList = fileReader.GetWords(path); - var parsedList = wordAnalyzer.GetFrequencyList(wordList); - new TagCloudVisualizer(graphics, tagSettings, layout).RenderCloudImage(parsedList, imageHolder.GetImageSize()); + var background = new SolidBrush(Color.Black); + graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); + DrawTagsCloud(wordAnalyzer.GetFrequencyList(wordList), graphics, cloudLayouter); imageHolder.UpdateUi(); } + + private Tag GetTag(WordInfo wordInfo, ICloudLayouter cloudLayouter) + { + var font = new Font(tagSettings.FontFamily, wordInfo.Count * tagSettings.Size); + var textSize = TextRenderer.MeasureText(wordInfo.Word, font); + var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); + return new Tag(font, wordInfo.Word, textRectangle, tagSettings.Color); + } + + private void DrawTagsCloud(IEnumerablewords,Graphics graphics, ICloudLayouter cloudLayouter) + { + foreach (var word in words) + { + var tag = GetTag(word, cloudLayouter); + var brush = new SolidBrush(tag.Color); + graphics.DrawString(tag.Word, tag.Font, brush, tag.Rectangle.Location); + } + } } \ No newline at end of file diff --git a/TagsCloud/CloudPainter/TagCloudVisualizer.cs b/TagsCloud/CloudPainter/TagCloudVisualizer.cs deleted file mode 100644 index 249d154e8..000000000 --- a/TagsCloud/CloudPainter/TagCloudVisualizer.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Drawing; -using System.Windows.Forms; -using TagsCloud.CloudLayouter; -using TagsCloud.Settings; -using TagsCloud.WordAnalyzer; - -namespace TagsCloud.CloudPainter; - -public class TagCloudVisualizer -{ - private Graphics graphics; - private readonly TagSettings tagSettings; - private ICloudLayouter cloudLayouter; - public TagCloudVisualizer(Graphics graphics, TagSettings tagSettings, ICloudLayouter cloudLayouter) - { - this.graphics = graphics; - this.tagSettings = tagSettings; - this.cloudLayouter = cloudLayouter; - } - public void RenderCloudImage(IEnumerable words, Size sizeImage) - { - var background = new SolidBrush(Color.Black); - graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); - DrawTagsCloud(words); - } - private Tag GetTag(WordInfo wordInfo) - { - var font = new Font(tagSettings.FontFamily, wordInfo.Count * tagSettings.Size); - var textSize = TextRenderer.MeasureText(wordInfo.Word, font); - var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); - return new Tag(font, wordInfo.Word, textRectangle, tagSettings.Color); - } - - private void DrawTagsCloud(IEnumerablewords) - { - foreach (var word in words) - { - var tag = GetTag(word); - var brush = new SolidBrush(tag.Color); - graphics.DrawString(tag.Word, tag.Font, brush, tag.Rectangle.Location); - } - } -} \ No newline at end of file From 3a04078d33bccee58636e45e7c9b86a3ec2e1c93 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Wed, 24 Jan 2024 16:38:08 +0500 Subject: [PATCH 07/10] add some tagClound pictures --- TagsCloud/images/flower.png | Bin 0 -> 75782 bytes TagsCloud/images/image.png | Bin 0 -> 57714 bytes TagsCloud/images/taras.png | Bin 0 -> 91006 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 TagsCloud/images/flower.png create mode 100644 TagsCloud/images/image.png create mode 100644 TagsCloud/images/taras.png diff --git a/TagsCloud/images/flower.png b/TagsCloud/images/flower.png new file mode 100644 index 0000000000000000000000000000000000000000..97b037e0f3bb2d07d185e717283b1805ae306fc3 GIT binary patch literal 75782 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 a>|+P4?%B^CKbFdAl(we+KSdf=LH`8{Y%OL0 literal 0 HcmV?d00001 diff --git a/TagsCloud/images/image.png b/TagsCloud/images/image.png new file mode 100644 index 0000000000000000000000000000000000000000..33721be42bfc9aef50e1e6e86032a25bffa2a069 GIT binary patch literal 57714 zcmeFY_dnb3_XphZE}eASyjxYJMeQ2l-PWj?+BHI3t48cSlB%|Jb>`Ls{7zbd7mJa{mVbD_l@o!IZ_Ebv-9l4!81OO zto@H1xfpQpb(H8L5_06oUrs%(`{p6`3j`y-sV07o-$d=8)*BDsOE}8yy_XIDGNx7X znm^K`S=DWMaY}r@5#PXIz?XIrZ-x}$&l~Tev+n0F#{jGpAo~^>AiXAGMyjC{R zv<*g&SD?Dp(ggC@M;rSy*=PDr1U;;qMS{mNJD8y8#jKa(e=Or7a8HBdTSAlS!OP5+ zxp;@sr6mRP4#L5?`2T)h_K$Kk2oun>a#Fqq!Kk)GG}P=_ka@aASZQ&p8cZ47jMf5f z(9~ip@}^n)gHsI;! z;85D8?BP^+2#dH}UM)&t`Pk{U?VoZ(BfVgRtGPvmkxOxF2eQw9prjN{{g$6@eV-*1r8Esc| z7Xn6C|5G6NzQ8=&HiQT3TFAQ-+(l-%SB@7|dGATXS!A-i?w1jA5K|pPMYPP-3O=*|NoB-hUa~oB}_5l7>F@oEz zw#fjfm2<3!%n2r0_oDRSqIqeJQ~&Vj^(vE3L|x~*HAQ-aXX~-ph zU5Wg|6yd4T4tjlvy%bl{=4{vNqAxptjHgtz=xU$ST!!w-EZTC9(sm@?7O|?|=^|2v z`k5SiYMut?4rK|CxpB^#UsvAWB4SI)kzs#eGcNhnJF5-7Vd}ouSAQ$4m%Tz5HLu(a z4gXiB%4nDA_-rp~ z+B+fOvyEzX{dWLDThn=X#b!{GA*tG9=efV#QeJ-u{|Pd1J_AHVSN?+0yTspc@?boZf3@!5 z60bFb=ygCW=}|8-4rF%S!G3=!;-zrt8s=~PEtBxr5)2$EfvFSfhfAqQE0YGf0WcN+ zL5aQf&t?=(VO`+^K`+DKYjk6}dt`!KWcY%GA4d4T6J8TrfGl?Ux9|o*x8L2u-yn$U z6+&OQlR$tRCcVm9iotz{tE;we;o$w&<6m|DhG2g<10MGxV?MWt%J%QBS;e*aj&q+d zBW^pfnzdh#=8IQ#Zpx21+6-MyMfT(!ymurb_K_d%XuX-eL*um^mo>LJMziad=**^r z6mtludxuNAr@qDI-we5bjHD$ZDmXf+`xJ+ZJFJQOevN0Q7s<;TgCGN}g@W(3kx|=> zTgvY<{K8ZMkx7j`kh%ZKuLjT0XGiz7P-SzyWl+!p&H$$=u<~-yC$C~Qg`@E$=9|{ zRQStG_VXW8TbE1x_-EwX^)Ojz5NTtMlYnTg@Wyh?)T^udl6X^s34^G<$&tsaIlnn0 z{O{xsYA!LN!Y0t4R8}mt#tPGdc@mL|j4EX!M@Y<%fzN7kK=^Le;$xDh1$`Jg4S2iQ zXqQ8Hq-&&>%mWNJZ0uXWwoir$Zv@GdMQ#Khu7l+xB|hpG8F-VhNN|Ro&SMD)A2pL* zw8pKLsv+jCg6*F8Ry5)t6{U4Iq(i%K$AuTl&Ev)QwD!?TxKyOi`r3|brN_;wvaSz6 zyU@cJzbr!wJBMf~g!=B9kMz?-GdaXVg*nM8FQqLY#T_o&;>VcvIWs`g2qsi6KBLi;pu`&Ef-RDAZT#^_LGu@Uq{OLx8Fq{fj%V_kO71 zBXP~VfOhGM5IeC9nU>i(PF;OuV!X=XQ5BIpvg%_OwEJ!;$3#b%e>cPNUeeHPQD^3Q z5fDjp4`o@yr=f1@Ys2!Yun)pa-n=&;hNXH+tyX24a8l!#;3#WPtH!#n1Ya3KMEup& zHv?nbD!-Y}iDw6SyR}(TLlL7?ehIZ&Rd5-h?u^>Vw}1h`C~Qb+Pp7&!&tmkG>38HF zgxb1C$A~>g`U>qZ7*48!@8I?#$H|WX{j8o$S)I##T#|lUOsCIDD@R1lGF;5ucP@W| zQ#IoVI62FJUhZh4q{!;|lR!Ap%_`ChkrDmf>#7Vq3#n-%z3x9LjJ%XrY~JA2 zU`c<-0cucRR?sp`s%xX>UH96=YwyU0!4A9sj zGqcDyRZe$o`Mo`KrbwK0*HIi*z%6*bpjP9RW{VlN`A+bK#Mz!mOz3mvg}=g|V4-(Y z!w^*wJU66>=3XTo2?;x>*ImB}7Vi4;zFYTs0m)BG9!&h$&$#NR)`&iwalKEPI@d`q z?=D$81=t%u^D6hb=4zK2e>|B366-}UvTo0M89T#F2J^%?Ry)vmFOwIeJ-lr~8cJ?& zxzd8?k17=*9|7+9_RIN2kodp4kX?{JeW>kUp$RDeV_TC8CGM1ZW^ zZ1&r}N%@qV`sRjj6GX}YYju@%b_6bn@ZEga(|-H@LV< zs*N6;@l@?h1g?b5{{x~ZEO}vuv|KryWK=@lQ_UcRJK=W7Ya#kXU3n68O+Om$m>H+g z895Zv$oC!8De5y6RD{fM57ibk6}j_Kx&96jY>W1^CjA$GY)u@%5mf0W8324{gpzO6 zya1)KLfZy2{a*FtLMDAQ9T;Hf#$YmV z;P0zg%;hZ2c2R&@X|nh;N^YZB#d2F+u?bo+>7;JaPNPF-H$mbPagj&%1Jfx;J++!5 z{oU9tFnx%VP9{IGF}Z=ErId9=|2GuSgA(m=FqenFU*f`}IN(G4QURc0#yc^r&2zQw zPTW&KqI~VxYYz02%DHhteK`s>VH(k)m-Ay9PacL@ zjSfjUe7U^0-aOn-BQ+Aa>e-vp`3GtRgh#6V7qKPaw&WIX&n)Zv1Yc}Y^4Wiiy8HF z4-b~)7QHk?9VpiMcox)KFB2bQ>XM z3)`#S9-%gXwusfvW>p(NeU!k2Ig{6PIdT-0P~Dx{N|uY!NrTUdrsgeAU(cS%Fk4VS%>T8YT_}tRw~KMU-eQr# zaiFzcn^db5I9wJ^fMh>$qx0bR0Fg6pRDHYP>wQpQ%tE({TQ4jCHNLrXe)vT}u}Jvn zxp=cqUN@%do6+QnCbd+_ei}C)_T?Aj@MXn{n5u;n@i90?7!Qam_3Uelj#ZIn(`0Q%q{*v z*J~eml0v0?)5fsMhI7^W)z2{*eEmZp<(*(aY|Z=xsg6XrOr?l0{*??>9gWZv%|&c^ z)1tQz%f2HHU(eyqUS`Mq6H07JiLXD0k8zA&i`worlg7p0BTrl!_XX@1ya|KUE|{yS zENK)OGYKE$DxaeBE6wpi!h` z8;=jBw>8>5To!R(X^>Yglzlvhf=jT*Io+@AOfSpg>4X+3XC;9Y)FR7n^u(4 z_Gx-k+D8UGISb+R(HVs}YWVac&*@JR*QdN)YNNtiCV>~as-ar<_e`{_d1NDY%;Yr* zR*!TQN&3HZ4-_8Rw9@*I#3gDL_QXL(`+exY8R|u$^kzb5;u3G^>*@*kxx$!j3{B(Q zk@BoLAzrpZG9bOFoT`WKOLG??wG{ptyDEeEL&;ULDUS(O7zm!4ZsOH zHC5--Z-#BMWBsVV-!F#a)(~WO`O;Q!FnYiYu}+aQ=3WIUq|tkyYBBE`4l+m;AsMm< z67PD{Mns0k&SvO9Y&br!3LU#6%t?!lXR=7C@Yrjkj%U~EepZARxbhJJo{5@kI*-DO zN~*6;kF;won5gMPplZ1@6mm_r%N#plmgBxqttS2a>#oaqIjJ#R`_SSV@Qu~|?QMr0 zqg{6g7>xi4ePIs?RuG8_q2_~L^#vIxKBlQqO}-Lry{VxE*i3&%ubShDU0;+57_a|6 z1${`?_^?}fJbMR{pFEgZn^LxfQIT|{c&1JJXLu1oFk=53 z%j3n(1I?3?Dztb7buomq6YOKPcU@dJ5Ls;8UL>KNz|}_${nQ|^8kF?bG(CLNRiK{d zQdBs7g}_k-p0qz_)E8-vULX}Z(Wu`Fhf-qxq@Tvq{B8Dkjd6i2F@3wXB*Zq~;l5D+ z=%qea9%1@=G@&j@m+|hd>bgvyXx%I;=~LI7psc3OkG-YyGBSx|!iJWY<%J9GUe`G3 zM$syoi?b=QVR)F5PY%EQjg)K7971EbbO}_y|HOZF=kV)3Im3yKmj*)9`&@Gc62w8* z2(2sY16~G+1l)zEtKaVy@4d;UEb7Htq;xor3)XjW%zP?3Q>2C)ve!TS13>m9k=c=%}+*JJB>c5ZWh$qrEq zYUw=wRCDosAYoo)eiMo8*@8LABhX+=jk=}nuLn)HBbpDLURjt=q{H{OM~(*Mhg-)C zV1S0nEjCn@>=}nE1%sKpL#=>>6Dg+CLB*yBfc_%-{pI?!BH_kMVFfd7B!2}lNbkUc zaW#zgP(CTBL~Dn6#RC@Kyn}rf8O9s^;;@F^JTZTyao_|!kQc47^G<;!xyaRtjqZ5* z{fqD9-YUQ7>TE0ej^e^!doEB0D;pG%sU^(8U{ z^;8e+#*e%6f!Mvkw0tx}7B6{DD_IUj`psD<5WNj2?$*_OS(^FUG!ylm!?ltpa}5Up_U!v z2AWqqS{M`@j6+bIwK_$5uL?-G0X+9TH;k&6jCuXN=gNU=svJ7~upZBX<`HTV0tk29 zjTn|;YmA(<*Y0g`f0PcRzBQW-!@7QNc%NZ#>ur^r;}*rg5jfq1@Ow8aXxGSnm}x{L zom{sM`u|WOZ`Pr)6%_6R9cR`n?;W?%T9w>8g&PlNq+1ABBT|G?&GM_n;X0a*Ps3 z)sAm&PVgGpP#;Ff?LSvedB&3>-9%Ae(LEOalf)CnvJb+T3s~EYt)!Hf^$UGw4ICLC z`yVjn^cSWnn|`;lwiwdNF-W}Ui$n`er7+TZv!TKnrS<6$9Sq?e!+AXCi`_-2C2+)N znQCm<%<9F`CHXZzCZ2WS)a@uo^x5{?&umaXtbH_&4q&mncXL{T3e{ge-_%ZD4 zu3T6~T8exO9IK^_p#3HGs|cp;Zr(-Cs~YXUKWgj8MSL;6x++J%UGvK=taO1&8Pj-I zTlTvN&h?1vk3o~hrFvz=zrfl1Bb-v2?6i`*trY+=aBD0d>XyMQZ-Kd)>(5AbHNMli z=^DW;*9a4=!E3(8-VH)SB){zvqT<2AoJ7XH+?>5p2YbO=FN5QnD6ZM9h=~e^ckllK z-Zqk0^flSbK7(fv5Mgdf16q+ z?IXhU0VajU;892psY|Qb?k&TcK`aOhe6^oCQU>!gz2qx7GElhFSV;}`lPUqWB(*y4?=>z=S-~h z&<0P?Sspfz1{9& z1LpFNDa{8wzqvg}RZE}<3coz%?12#V8t~amD=qkQEZW<|Uw{bFGNRyimmeiINN^eQ zV!;S+gZEF|oG`?0CpP!1Kle&^s4`B$wRQ|G z<#;4PE==$JOUEQjxC0Va9Pd7WYJvtiC&rrTkEY86>jjweg_9rKTED;r`CBpyLWd5(kRh?0H!0(hu-9NORA!}J1s9>f2#&d z0+;hjtLt<=(M`>2TRTbOvEXKAeorIP*)cINO4Eb)i>@Jwb1TV2IIZ{re_Nzoi}1rD zdv)SrVKB*K&BnCWpFXC{@wY_mC9lqnn5D>N&Cc#k7t&uV{O*~|{etkPFS#LQa5=g# zJ}wEfpDRk#=GvHWgux(bHagBM09Fdt#}~Mj_pVj}nszf45`RB6E=jiUROEZOJbQIQ zL}b{Yqj91XFuE{XKofUR7dJR8J6i@ygoLgm=0U&o$OVELo#z#3gC1%W1tpm_RMZZlp$gVteW{y;s zy#x>KriR## z;HK1E=0NG!pdyQX?7T9zRx+c*iX#Lw&b^qQbsp;7T}zI_IgiG;%O5PYmocC6+cx;h zl5%tUBrNh*HNvp7u_-Nx*?g{Krmx|Cg{`1|0ZwpC5JQPhPW0DL2dX*i_6TyeNQgO> z`OP#YL6RC=L~8(P5b6?kMK> z4PpFGWHT45Ik&hCH14b?Lxy(7lER9MYSTg<`>0PVjKqc?@!c*{cv2^&$UEdbt||M- zTw`zX$Kj#sb?KKQ-@nh*Uu-kkHIkN3`t-o%dR+%XW6ZXe;XVw03cr29*jRq*5lGk9 z-%8vSD<0RY2v;*B%?H3S8wKqV-`|iw-YdmTRE6;TnFSljFzjoh!qZj0CEwJE`C6eA z&b&^ym{FodK660SKPxOEe3nS?s`HiflK@?fmyihjS?VjPxuRN)WYV4T*1h-*SE zZ#m!lsV9#(2Q{GE3C%t-_dkaF!-vikx+ga3HZ+TiN2*|pt(eV%`(4G;O95}VVf#A= zzM|k0-lpieB2-?2ckii9x1yK+63Z(iRW8&YB7P1fH|kXQ>}LKt43btga>`&vW{-q^ zZ9aXStl;`vLd|q^5FAm|_v^YpwppG}#LJ%t)wL6J2E$R~AEFN4XY&5I*;%PKgms0I zf9}df55yLfTyUd zy+nXfaD!lxw((W{N`1eAfDu~UA6No5_E>_x9sobHmpE`w>ylO2|Lkp*Nk(zBbmTu~ zS1ocMw~ey?h}^H{DHK=_y99(8w#B!9Ra_+TeujWlom-@W8{z`q@8Rf!GR|LV_{kJ= zd1jv%|AMMn@N{f2x&?>|pH*=yHi0b^%>-?!Akf|ZNcxK#99FSg)1Q9UPObdJb~JcO zBLWg?M=k8qsg3$+FVw_AH74SKclRW`YaN7El%?P~<`MTOCv35^E4=-E;AV?e5>;0# zpe)cq1N+KbHs$f2eNRpzg z7~VO=?Kh=>j(?4D0SSsw`0W_uLt#2h6X$kaAdx098 z0h%?Qx{Rci0Iwd{wCo5Tt?*Vk=k3oBbP^ej*_{g2WS`xSBG1<&gz3(GvG1Cg5)b9=GucV1vgcJe#oLa>^6Lo> zczX#KkXsiOzNM|LY;uC`oV4x&7xBh{zCOgpDA3Jo(Zl}U%=xy%c1_FA68`>c+@s4I ztDgbmvxVjL=(?EjYEjweIzsiq)*E4$U{Phqm<9m6s73q*w;nB_6or}|=^$SU9!gTg|=Vdrw+=^Z^m3bD{h&Fk(TBjMFZS8fuT_6(E(Bk8!R8l4? zE>wQ`z;Q1*FCDK=;k-i0E*L1SNv{W%*b0|^-u{?&G~W!^$kHBW?MD0A8|2XS08QcDIZQSN?O_M;tsU zEV8j9;i37F#Up+pf0O~=3f*A_gntVS7RWDU_G{ki=e-rXL;0MO8#W5`5{@V?Sf&{X zH=jCC2KhL0y{h`;fa&GzXW%FlvF2~w^74gH6qeyQ#~7eWlJ4;roL`EGgP!zT#cZCq|Z zikFX!@j{Jh&8`h!LGSE(pNFlMvTI$nDLC2VuN7`^X4h0AR0PzxE2fI0P|HW;p~b3Cmgr9cSRp+5!3^3KHb8LfSL<$r z%0icOaYS0hyB%z5b4E{^h3DI6q=U3#GuIL`Dvh7XQApQ^U0RghEFWARc!ls~1=K_u zoTO47Y>@5cuL-SE*i`VVZccaYt3er(fo(ViOjV!m&XD^`mYf zvb}MLRXVXuss=9mLQ1JZKL>%!B-XC<7N3w&J%vXlSPPm)yBW``5It?JL3@`K$O=A7 zM13)D(`s1gk9P=vI4Em=_(0*x7P^LVdy+SBo>v!lR&lrc7wqGZ7)-I_z4Q z{M_GclF_g5YBBKS-saKgH>wvQ+HTyAn$UX)AszKa5D(Q0mpEoYzSv-SG+0?aLsD^w z``BQBvi#I&T^8)?A5){Qjey0j3EGQ$OQ4pcfkY5+v@%39^5K~g4KA1y!SA!Xzsu-+ zdh|MR>V{9wJUc{n-K_QZ!v@?19%s%&9wNrOnzFE36Ws&)j`OauLJ}m$l@lXbhh(nJ zH~QtHK^UW0R*}T^8+uKkJ`UJr((~6%M6U}4%GvFHgZq51$l&P&xjNF1f2og?P7MfX z02`(x2XgM(%u{OsG1HqI54#117Oj|7(xBaW!p6;ZDXE#`yoK@M_WZHbXAc1G)u8XT zZb%PVlL^lrz23;I*Mo|0ws&AL+vsSVJYSr8pIP9EDU7VH{s?>7AaP17_qkFjY3ufVv*T#qj z@nuV|rwl%uT^z{z?&0 zs!BZ2UbmY8?+>EFq_8E1_v~(c;!(anE#nKXJN~;GdL-Sn9QM@`_OT48pH8{18CpfV zW?vWs-U{RS531py|6}J zhLes9WKw3aD!29@ch#UAj85CU)HRu=sE-q$9=td|Sot#P=w)GXKR$4@6X8I1S{8 zE=jwY{_`j6`mhkAvFYXSH;NS}XS1)IG9 z+wcJ%bv7={uay)75(XJ3dBKy}*B2nkerKFBd4MaGGQSmqU5ZdQpQu=}+WGqC;|n)pqZzh8VSjeYvdYXBshmtG{@5AtGmd zuoQ4&C??s}yod$v!lejo_-7~$1&Dx|r$X!tz)tpQ!9c@VozA6By4Dxc33|fGIh>Kc z{8mSoSdbFhwyw<9O-289CJ`324P-y7o@ z78~q()xLU|wZi~?L*+6g{MUd_C}h&D@5;#3QN)TD zznIFo4E!BG*$XK6Mv_lWDU1NhlI|NKFCt?44{+j_ z@UpFEm!2zZWCA|T^67sGxlHbR?{UaLIim5@_d^|{!*Zn{CPHA4pv!*zh?m0rJdvwz zdCwR3%+vkJ=V`fqbx(uBKFU}h(D7x#X^Bv84F0KHp<0AQJ}1Wv_}w!S&AqL5R&`qZ z+#1g8m;Cl%hkKxER_>xq%-C2ZPlR~oM*I{nG+xaVk0iwx>f?Vzpel+<2ea@2O7 z`su(-KZ+*+a9*+SE7cd(<_!P%cD2H=?D=`0w3Fe=?;6ag%9D+)ktD~fz<*x>N77~(h4w=x`f)5TTjb5CCwVA9Am}~ z*s$pXM{8u)eyu+~Mlb&na4pL|y}_p8t-)*1`gQKMg1z^x0Ay%Y`H;{lw&Oyk7{t!=^T{PfvD6ae@9ylCBD`|}^ z!fh+s%6;><7HqA`8-AK0t*e+8BwvzVxZ*yZOdWSFwMYp@tt;6Q7mWdjB%$S#roZjv zRxVFlMtR?;-o=C<!?#=Wfz6wODP!pUX8(|C-nGtMsnhuB%?I7`lO4)=Qvu;Pf? zrYa3H7;6E=8EKPQE1CG3C9h}@ zZAObMXhn~c@P+8{c<}!4i|JnWY#i~46O<>&TlE0-Km{>ZA-?Z6YQhbEI8>WeswJ&y z*Q6nHZ+s{4Dw+@lu4(gZaTH+e&cxhVJqYB`xb znvL8ku$dSx_H4XkQ74{5KV!9yXB&(veUVqQtx=vlI}4#t8)Z#=cZcd4BWHI0*k7ed z<|MA|jMRuN_Ugz(eHsM_D2v)3y$}o>Z7|yD7QL7mhF(7rx`Db5a$7vX*B|((OUZu2 zO7vg=>g6L!sTQxT_3733l-w@NWyo#> zP&_I;aVI0)B7isQsV#%!5Wu@v?jJT5QXaPLl_>V1%++$pNDchB9&CHdmdKg5bQbUb z5|^^o+u7@0G41T8uG@wv9N$~}(_7h-8w8pk0e$H2Td;mai&PO5MGCoWvCOjtQ>$b_ z(d2mUG%(C?ywC4V{JfT+dbzwkNaHzy1ujCF;Uxv`W^0bU{PX|qzBKLKX*q{BH`S{P zH~F#U9ZFVdT~4)O2g5Z5O=@`F#qw1Ihn!*$SJVe~Vu&uIge5Jq!R;Ov-DX{R5#zxk zV=?7!CAUjAEbYWK!WN}E*O08Y3ppPfZGvFNizT~x6^D)dIY8DwbOMHa*V2R3_~ZJy zk3+XGMYoy~7!&p2^BQ$Y9%hZ{7H{?lw^Xn)w%KMapOd2r=3azJ9UJXha5Ut%MIOIL zF{z~B+-K{+>ZHIuIJ?P(y3y6eP`d(GMo(9Y%C@|>W|YKGgKQU_&TOlM3cL;M2}5=I zXPFe(_8C~0+%W~aqVkG{;g|i$$@#^-$cjG&c2{*gj(H-=sr z8QG$ay=@w=e3PFl5E(5_D^u`jsOK@-cGJdn{R8Br+SLjpnLEKGSN5}vXQhM?z4hD*_@l6@46g)B^B?6r7LHM(?w7wAZ zCy!&nheJAHNmxD;NAOL0J3?iT%cX{!A795f=q#KZ8DRHqF_t-+Asc8!~CU?btn2 z(M;)f2+caot85DgaHym2wtZv#jrpxjx77Gf=xkgDepl~p&sZAiOsO2~xK`GP?ZF9G ztaJ})N;mc3Q6JD;csA4R&-Pbzs5hJN=aIY?mch90lXKd=pIEbLFAnO~ zjBd@ctzq8Wo-Ha$bDqi$h9A5h@}KNwLuJValH5F}*lD(zE@e;=#jj8HSwOiU5=X?o zz-DfjNKJ62`~Bf&^BL?JIXE=>n$Ny+yY{EIuVQaY0_N&jUU(U$P}Tj_ot`Z*lEdl|NltkYKi;0g@1G->-O8&W z`C*e5npYZIN)c@4{LP-cYovelNv}<&`pBwpXT!oJ(1dBf5%dA5Fi9HV&;f_ppPS@b zp34rxA}wD}l69qaE1vZ{6!LRe-wD=XC~kt3xen6ONgHZZCi@Ce4#h1vBK9c&5>!px zQ3)kJRm><6`a^`^^?>)j%|d+9ReW7nlM(hpE=fI8a<2`!FFR7yt6Ay%%h&Yf3L z73l|x-V0vGp!Cq@*~gd3mYAIr@Mb@=|8fx9GPe)Rsz<4NJzO*O4zFNFjtm7fsbxa# zG?Wc2Ov3qmio9|Iq7HGfm++Z#TA^K%Vnu49e7MsPO+nIESpKgw(tA(ifem59q6NF1 zy5+KXF8Wr*$QW~dr@Y{_+vTihR^wkYfsy=HjWXo0IWL=G`NLvyGOb%BMEDxQi439; zUFWRNN`i=yiCvw4M0Iulakv_==kAR$JOX&gBVZx7OT4hzUgr_Hl_T>_JY<5OgFHO|vQ0ctx@-dC z*}Qy8n>XdAmmbt_c12+W!@desXJTH1-!bxUta9jmYc_2Bb4Arj^HM!`k%7H6Vxz5f z*QxJ|2L4ugj_U2DWO9T_$GPmizQ@IU^3%Ugo0QolV6Q+-!FK_}bsseOO%UnHf5ze_kFlkLMH5)qwmB< zV?BK}HZ^$axrqt?R`uUvLp=8LIQ#dlS;38;VZrjH!SGTAY0UjyijC3eatBB9i@$-S zru7;%3IaxX9l0jQNe5E-Fxaje4zr84S&NO^w~P!DQmA?oDKMB51KIp0-3#1lWVAbe z;0?&9OwPhNgak^Spwcp7_D?(1Oh=IFlVRLoOJFJZA?;?F`rB%~M{l!>28|~`#x}i? zO`{5L#Z6}>E-r-qZDK_dOp>U9NwU-O1dVIpKA^Tfo-(#U}l!{dpYUR1y#jLIc|Gg7CPzL6Js}9(vC6B zS>Q-sp~7S(;Pc&k-&?NvEKN-p`xz-fZHg+8%<@|{aBFJ&`@W*T)StI78ZMLeSVJa9 zr@X;rcHLh)X$K&8;ZlOj3gzLzPJ*hhSl7UuLs4mCJjYPeg2u^3Tel?2$@YKm4tePH zI7(&+>A}9ei!q&nL$R4@piooo%<%x}5@bpLA!^%Vu4PX4EmrZ0OH2MS#Y6pB@$RPC z^H0Z_jSQ-s?(*XK-r)21?L{n$4DVLdy#ICmk|AM#XoY$VWzMSqn%8<6S8EFwzrL$} z>8}xH#Q_CN(%vxfR0vV>>G#)J3DXbZ^na^nZ@;(MieA`m7_{nR7gJrF)hkg~I-J$N zS*NX-65bnEEkl_|PPfs%> za~M3$O78kd4VKuru3AG0#%yTqOx*c=d(Gsj@mbWP%Jf@tO}avGE+y}jUq$KBt&wtM z7+3(&Nq$Nh90uYvxgtcg(mE42JigdTHe?DHZj}8sdgCt@{A^|JB-B$Iv6O9H5>9hs z==|7Pi1ip%YkU;ODBTR4YR3v6M!(O$6Fc{|QuAh}w;qswbx)f@dyyfZ-5ns%t*fST z&3!_%a4O~Zx{xPB%Rge`VWr9*56~{Y72QrxQ1bDnD8w|rUJ7+#Fx3Xp?_xz1l%Yuf z%8*iEO-y>AY;tyvcjzjCyZo!fCEEPMogb$DZXq}uh6(o4|C@fLOT!#?91ENx($z-2 z6J<~?Jo-6n7mE=e02b#WB`2_VqQcdOYM~pBh7o43_(~z($gM6#z6dtKquDi?T%NSG zndu!mPk@!bP%X-_c2_;;UC~BY7CFZ3y3Abck?QpmscAl*0gDKRRkMDwOdPTgmjC|e z5kI6i=b64jv&c3yI_(slpCk;3R`yuKV7DP58{Cp*`5KD zHl*JHrBb>zkbnBV#RTtP6tmgY>)N*g@dCotQQ_C*eS_wo zQR;kMu8m}Q>1RIi#;j)DNe%1f)nkV&4oJ<%Sntu$_{~S+sJ#xAcb><)id^2`Veh4> zfs;D^Q|Uv!gEpE278J*FU@49M*AZlCpOsQhmUsR>$R4lKW_J848;-0xx+|7u4`CO- zJv&PKZI&3qhMT{gmnDk{h%?&G@-gTLhKH%o3^&S$0N>8YVa2Q+bPz6 z0Vv<1A9VdiTpu-Qvmki?$CEOnPSN)G?kX-ktzP|km3m7~dYVzJtKRs;pRL$u(M?u7 zas4{Uqcf9)7k8CU&@ITz16M#7>a7geRS+06B9 z8~}bNIAJ(zNTwFAi0il3_{Lbbw=RO4xnIEc;eNA0TMRMn9`b7OT|j487|n7cWemH)H~q zwqV@(vj$Q*9CWspfDM9-Vt4JfLIgHh-GCJk+|T_l{GCs z&Y^t4h;qB`AL2du_x2=xEe(tLCz^caa|#OVsfEkNUM3fIYUX~ryMfNw2YgQ2G zu?}^zjRBt?N762fQrL)8Ns~)+sA^v2@S~pYO+fisynyTUbgcOn8(5pCV8gA3 z?Y>GZ3Jn9DCHKol<%~lfjt+6LPH%!-0+Dx=nhk?hqb9;~rCYy@T@ToR8Q$3&PbmTkD}jyUvpeWgAKAHs zcs!|VE9Kz$Pqg-_anXzKL*{H><>GS{Ptd{NQZH@b;Tn{)$C>xDKc~94DMDZycO1f9 zP+Q=J&$|7e#xqq>k=lhnGLIq2843|q{r8U}yHX>TN8LK?^JG?AT`DV(~=dDEl+l*_e zkqwite3yCqc@?`_5S|b$D^6$MG}j60V(qM~)SjZ&XKKAp65hC?v*OUqyxaE=ZkvyN z6-G!yJKV-KdP$_*X*{3WT~ylL9BD0CP?1&6PFr+^SO$$*`ui7n?HhePF*Y1!poT++ zdOg;?L9O$#ylnllVsvjrvLqL#I82yz?PqQGJ9g01rVs9AFmFpDh9Q?3yLYTk&{cc{ z(_GK8kZb`OenHWrE3!xgzjA4eWO!hKlnN^I*HiN-nEWb1E z@89{%Iq&zJ_kF+bcX^)YXcel?+En~hss3fH>H1^g#0&K!4!P2Es)7x$opVBQi&4!K z{k3#?@tEvAOsFkm>*^M@B{ueVBWmcRWn;Cy@|Wy0_CEOHqKPuo z6t5#-d0?2w_kt28VSbqs`>_7w zK<8!kIU`G!+(?vFX{JW$%@j)iB^R4A<`}0-WpU~DNzoqMrx%Fhtz{F<1CClJ`#%$O z!Z$D{P8_*0Z_RMon!FLmjp~GI+t(zW@Z~=%L^Q?E_Nn zJyCK+B4Yh;IP!^o$msE8BQ$4(n3W_dF1eSTa2TWrL_UrAhbcuEso=O>Jeiucekb%JNN1ykEW3eynMZ|_bS|3ELOy&ujq9^bkkm&S?T-(?C-N@tOqcNJ7Z}$2c$Mc`s#>6r}B7cO86BN!foi;04 z1X1Y|xha+%p2H)x^`I`|WJFvAFi zZB{#mF%E`whMl{AaKY+dwl$+rZ^*Sm9=a2-VQ9W*3@KdF9l8EkEQKsc%TM9Pf9Wc4 zj^Wa|KfB&#fYn|EWr2Op^RiC3I`cgM!2q5J1&2@^#mB_hyn>(Y5q-7vGGy)Ar9xaY z{Zy&dV8_f^>7xu#zg1Q^artS@O!#G9q{`!gB@cPQLnqeVmj>yrtdjx=!+vs`Ba);m zlh~fQb>r}4>AlR7i9g9oTecnM(bvr40Jj$C}{|Ip3lfmRk zPlHo+-5nXWFWQYP4KN##!xuYMqtCYLG@ig%<)>}2&f1L@nyy9X`k~h|h$Gs8A{27i z0gAvSJpwRn#4w|qYeD6Kstw?a%RamnS0Hr~;GlIb95M^pC+2P{Z9N#a*%(X~{^yK> zNvPg2fLJ6uyJaTl&BVe%o^uaOr!M}*npE&FEvfd^Fk;Z|S=v0EYH99b&kWjuXM76$ z`V)%-XW4%Ysbw^bVS4*m@Add)tn&67A4x+nwCkr<6$oI;6B&Q z2R=;ajjotU?ScAO;tU~i4sXHP?E8&xMRqyTIOmh9ck|qe3rnnmsOOq7r!fetO{R8@ zD224-26mx3Gh86xfMLN5Y^eJS4J`YoyUao2I(VVM+s;Ujq!w-B_7QR!1pQR^7|8Qw@JKOi1vKle}^=r1-}`i6YWiL!WX(NjiM zOW(^mE{Ky?Z|TCKVIxwc+xW13S^h;}{`OYRLL5$-&6$~ycDr|}pJ|c&A9vQ$%Fdte zuQ2MLOjzDf1OQ+s-Vj@~+CEe`dUU?c)3$~F)U^AxbsPhpqnX|rY|=6(t{N{#1pU(c zpze(CHaRZkx+|e&b9P=e8}u*8N8{=Erl_+@Z4^L@h}nC1aT6$H@b(Vt;Y`H8AiTNC zqqXy&yMTIe7oFCF2w#`5sutuk44#9sDF63r)Ua_AK)_XkTFFTnzpAqsyk~VR&|jRn zP!aee3VjK5Qo?TJ5vn{}7vg|&xmA(C^nr`<{}j*BnLAUyBDoyf z$Su1vBs0MP!V}Nl5p9Wt!?*Wc>`WzEbXxCmtzy4uX{L`_^~W%!NiHn?a{Pi|6~Nz| z(75>`OU7Z;d;lPIL~xB>R5rFWqvFz!R><6vUuqtIJ4k(t?Ylta=b!&k)TCcF*48gi z9q0SbrDh&4PwzbGzjEk^N)oB1xttWp?NI67%O0Gz2|^0o?-M6Oj_W()H7CHJQwC2! zVN#QYc1P*i+I~#7W&bqk$Yg?s`gA$On@x?%H*GK`AaeuIPEbq^fS7DNZy7#T(E=G2 z6a(fDG2Mh0pU;iQJD-{|iS3~;du8vt=!kN0Oh@B$AUQ_z~ZZc|En%26vbK%Rh?f`%+(QT&a~m^0xIrH^AmBG&lf%e5m! zuC*xfCaeps{}`Vta2)uXaK8sbZEDNDeHlH>vgxWldK)USos!4618->Au_l8YibAGrqj9LXzJjOd9C`wKG+mP+i>*jG#w8r2hybt%D|5= z=ajzOIe_yp6(alNbuItqO%b=?x{`@<9VuXSu>!jB?!W(ETVqHEzs3)5n7BnJ4I$?K zn%Dg{!4$^?SoBF7pRGTb&R!{sdMr2^=(tG~Z_QfAxsSNMIjC5zMF7qBW+6X?>2`gQGzP zK-YV>=VwtLpgfCS&TWZZY&f!fM@b=`@Auk;dLp-qluZ<&2pgAXnK^PVhoCST=b@}* zW(a~Pj9h;rc=PkR_dHM2)qBmk5uf$BQkTMe?pva7vmfeVt2Id2mRQ*wsV~en0AFV8 z`LZSakIfPGyx7~habfn5b8XvOn$-#u)KS+`0CQ3nT_))pae-S3xtlueC}ko^0JFq zN7w;#vkK4|Oklq}P#0vq-a6X$yDSL=Ri%=>^%+Lr2_9bN^Zi@j7EgJ6Gnx$c9Q}M>+EU{S>7Rgm zB12nEso&rY7P(#@8gjA2QV87m-C^F7`Wd*sTzNjw5p3xH60qIB?|VjvHZdl;RpYP= z>pWKF7!>Pd zYg&^&RMj(>E~5ht>`GL5fba3m)o((m%%fJ%3k0K{(3ORKA%Q$Xl*8-3(+}UU#|lH* zfTa=ioIMwnl@MrZAGpv|BfWms#8W~wr)_vLo9g8G%ZGOgW`Bx9D{z`_t`KRqez%b? zjyg=~3T=3d4Q}$|{tP@wvBhqeM0Pxo3B+6y)?fV5Vwk;v2g4cCHv@TrV)SY&Hs`$!Ly=i07P*jBdw$^X$(LhP{2C$fNd#ZIVAw(e^G2f=R+5#)z-1~c|wAlP(7hP^DS+}jJrV{OH ztU>@a!|5={%0M*1PU+bO~0Cf6uwudn*nY2 z?|yDN$3lQ*JzB9MM2l9Kgv7(5Do1Bk)iC+hV7b8&KhX!Q(FwgXcdJFin%zWCViH?7 zpQS@O1Xj%NW@=W&n!0QuWeOq?ZWOsEkIyRRLQH3Xl2^sFd2AiXjehGHUYFF?oP8=O zKX%B{pv=M)Ag-$J&5*M*r6KdzVxJ|k9<-_KV_%5uL0pRnA8o2TVuhFML>)?0! zKZt)HlPhg8oSs-GfT1#9zaVz=^t$IN>Wsrq%kZ>>A5Xm_lPL8;Fb1t)sZx%EE3aZCHEWfQ|+5FKK;2?@-s%Wn?5bjC|1$rdQ4DjA5IVe+9s@k8s^dze-Ec(s9 zM8j-c%)x#{479jyvpyddDnUG2Z6TSHuC2d6|DH$*6*6aaSq*MhP!hE_b^NS@}R zoSDS+xdT^u4Yy~&Z}xAR+)|u=Y8Fv@NDR4|G0_NqynL@mN8Movv5!qPME(_Vkm9YH z-oI4`9DDk0%>lc4Oe3@ckyWZ)bBK}{5wMTBxIjoBbVs`qf7?Wyb=slnt+Ed5V^Jp6 z#PS&*KPpBMD}A?m_sGmHK*MAGo78UJ*fCsR&#USlrIooOt5Zs&n2lnc^^x%D+-1WA z{AT&rzyP$1w(emH`Dv&pmq`3EY<4`zT}T)T>sfh6Y`yUa9IIx!JD;QHDX5f7A(=e;&=zBMkMl(k)*; zyY&3F%DQ!XzSp|DjQOgY*Z6FO`ATSYlN`42n;VcB)*Qfdq%c;u202Sl<6#jWN}X=+ z(+Ug>sTZ-bo$3ZbKma@zV44os3@zD~tvA^Sc zxMn^0tfEmD7)o-7yS5|h;beS7+Hgcp60Gwlj6FP@N-o?NLZAz=bq}XZy#Hd7F=IHF zhE0xr7<0i=iA$n;nXwD$e}Hc%@yqZBgK4qBi4U?u#l1&;IBualg~@*x*wyVPJut51 z$t%#nz`%UkC2Smn25OM|8HwUV30^t=8z}&_yP!IbZ6SvvU&17vj|EgqSF~Ba;aqQ6PdcGhF!n0K4(JH#G7D!jc>oS zV=8ZL?le0#SVx3ndx{=zt2}2kt{$=4qjGa4y=u?z9vwUO3Hla9Ih^=4WIfuvfXCff zmY*EZiX{jJi)kJth1doYJAj5_?5Ll3`Qr)Muzd4^iy>?cvceLw?%IYUdD-eP^$Bl% z43KIWgS*!mG{Mr}^#l)Ti?{8pdWwD8K!|Y1*ReU~M^2;pk?pNv03{Hh2m2Sy@)q8S zzhlxVK5EZglHPiS-rw_R0|+6Oc3^vvAp6TvfHux-Al3P>Y4g#(R z$}hI35@Rlg0-}&CpAhr%=<~A~g)%#hv7v@Z&+?TfSI#|vMHlic*{BB`R}Ua#^F+#b z!zeLY|DFaeZxOwF;Tk#YMZAqdkNPIvjePH$)uj(JS#aKko71m~TDu0-kN@?=_1$60 zja-_fZ8Y4Oy;Asi(rmHr*l9K-tN zcd>GwdxL9ek>u<>>c)L!3XiHSp4cFn=k5P=|EnCkg6BE!?7gHgI*&)fRYTrqt$*Ei z_P5$j>X&)+PG-BV^7&Z&&H_As+Z49f6vU$oNr7?J$i;$u$wnLd7_|UPAB-ev(lFh z5I?PE{)&8`E#L7lt|WO-NXMAn$qAJqq6$5khH;0Ji0;x)|xodq(p$c!Bv?#@H&s~jac5xeHXX&4EK76P& z1Qpa@i~o;B)5gSb=PvY*0o*H*uTIR#1fUMrQ9kiOH_avc&mmQh=-2uU9l=>v`zg5R zG3wYfDP2fdoo!ib`;# z-LOE>(1f?q;meV|yUA9q9a(nZoQfK{|Ehmr>hJ%*KbIqj1}gnT9dXH>l;t+Rjuwe7 zir8$$VGP1UfL2o5kk8^}D94@od%d8PrOtL_wJp?dYJ6HYiKf-*9wF+F-=sC<6z^I1 z?{DgieFzaq^Lgs7#RS~hVe&$O!qAK)Bpb}B>fkZ;Pw&Wo1j`7 zr3dQ9{p|m%eEsfcRcIpWaUDgf5?613_?n1u)F5E8q0;f88#YJ*(`h}4=Z9PA&sG__>#ZyhPZ_z(K`30aP#8S-B%X zo4A{6Z&{B(ci5eN&mC3uar`z9kW`ebfvP`NUJaEpIumR+yY<7Oac#Z4^UqGg^q+CB zuIwKik!eneHAjxdlv&V`xgr!mmVK-Iv2RR1E;mz)^yN|G_CbGf!Zn>ej%dHFV}n)7 zf~VZT31BHxQREU0HBF1o1Oc*KO5||KKe4#sji~=%4L4qRnlZNO@h9SykLZHP)gkhO zB`iQbH*t={u7YsF3rO!k&C{%If3oP0XX!vp-+m>!vePKvzRr=__PZ-|SLEjX(Ulq% z@Vq4TV|wPNMdqc|iGlq3kek22m%MAB zCcigza4pGUCH!j~24NKDhgwPhlb-E{RE)sY-sZfh+gNYe{3(@xr2_Gpkz|*m!ax?y z-JJyO73bI6d#=c0N%GW~&AXl-{Y6h;SgLI+wvrO}N@pI#2Iw`ctOVb7O@MSQEA7*$ zsXRkZ_4Lxag8=gd_G#LD=Mo4$Lb>C5+biJob6kd~MJ%9iXg*z>2+KqNhG81CxrP+v z&q=+2Z9tig`{L*}B$#i{jLYguj_q*L#)WbL3*aGmM31bgaUTnxmHDP?EE-Cum`?fB+M&6+%A=UR)jJG9hd{_)>8%@rIrxXGB1HYl*{4OOA|B;Gx+*5^u8k=? z)|o=@;<-MK?5|0>#7BcWv%Jv^JrX$X??t8_`CPO1?FPDpp=kBQ)bouH9f7?&MqbDLq|IC;y<| zeMMtG$nLVT>)NMk>&~CUNh0s|?$!8)f=O0#T zd5dYvH4Q7V$^$f<$?}5uPFy`N@uB+n7;RX^hOny5dVz?-BrTVAE!bnPXIsRf@b)n3 zfmjv2^dxPj<`6ayH&B3Jw|C z()aJ5BYKCT9^f&;f-_Ah!p=LyKj-Wp^w7{kVueb!?If-2rI=Y*_DvXg_;Lc?g8{!E z;1d%X4^fdhKnb*JuTi1{ScR{DnhCBJavggN6g1W8$dEZ_5IT|X9ndenhiy$cE{MMv z_bZtJgcReqz*$U8sNc<%u1MtPUX8gcE2Fwwh6$8pn-w#2`QCy3;(OT^Yd3RYAvTL` zLfXWcBa2x$&7HbbpCF{%qU~K+5E>+Ge>G1s2;E0*;GD*=j)pA#^#eU8+CS}NyLcg$?{7URYj>D`(z5ut4> zeUoed<&De{O4|dTamEtDufJ?B+XS*^9~LZVm@y(0)f|_Gl{X*Sz8>^SR>j#_&d46C zyx8J#Cj1w^S}as*{Aa;WV8>mXMG_ZIO9Zrs7G&@#N~cSw!-}`GkW1Kj{(JFl;kZ`l zBJv3vYDcU9-N{_`@WMTaaVzZpP{z+Gqe)$Iaczz&mykHHIdz*D4!vHKWtXq{8i;;< zH1=_K(!yth>dziq$+c&Vxr5F^4dwud%A@eL{4%ovbunxIAz^xJuYsDNtC{b(6*(&qv zPl{GX0oxb#f%iUS8m?FM&hj0O0sFl5sCA*cHFVa`SGl^MGCTWgaJNAoqG$V1gZ8UZ z@{JR$62qzGjmtk6ZwfYYeRTwjih;_K%l79*G_P@MM`{vv%~XYFfZ!#*^`)x)`L1L4 z!k2~5EaYB_JUxSw?~>^(K&s!TGN{}Fk}%FTu{eTOdbI&SButW1Qg-pm_bE&}M`Z2{o%l`E!a~h9G|Zej*41^} z0rXQ2-}%jjXO)4~Bn4t^HvoOK>$%S72+tDLzdcl4?v}bI?KJ{;pJ`iCR=+AnBa#l3238W-*r*AhRtG%CjrItl4`D_j@z9bk} zot-KZmT*n$%l(QZ1VOYAkMi(!8IvNGK%B9kL_A!oH#jEMyhE1B*DN%@9c7XCH|p_e zJ)H{**1D!i4d1tupzOo&3hz#kp8E|1I;A=8$gDwED zJCdBP>F{{1rE9QY8^l!VrO*8PCcw*3;nv-6TE^JEH(vjkMp^2k({k6RB>Hc`@js^0 z)Q^M++gmCrP~%lSTifbH#KAouthk*W&bX`~#9Dc0o1XYkB+nwxCa#5Z2IETEt5nKIzM+CcAHh&W|Y7jRe79} z0H!($1bZ05!_{5S)pUJ5YH1sRvaD`axySHS^^Q zD(r(o7;rOe#lj(Z!o};$YL^l}6_BL)5*10d@c_<$~1Q29t-F`Pc&@Ix%88Ax)cj^yY(GVT4L}olEpt?&v}|; zYE|a`kfLu_|h`t&zGfE8PDjQq~Y*RWx*5AAp)00Ra;`ndD!=y~>q!u05F zEAt*z+|eqFX7WwiTSqhYqmc`D#zoW`C?7t#-aLsx4M=w9`+e-eT0RE7?D{c3L_M(v zQDI@l3|o^g0v)Gi?P_iOS%t2N3wpd`xR~<%ks|1}Ma3X8!eTRG58Gag@9^s~Xf&cX zYA3Z~&;bs5y%lm!y`(?6jgFv(e{$5~0-FDqw0pWrXT=q!I)1$@s$BX$u+h>CCVTE- z|ESWsnYgA_5&QJ(xv#hS_{+FOdMWexuh0Vnp(pHw+5Sr!j_5+H$$0xfNX3*oqZ>Mz z@LruLb6S3Df2JXhe@(j-auSm+H(qXbQ3cy*smMfsmsmo8p5veLz-Ju!!bNK5aIKjcGGA+1NmiRC~3a3m?aj^55jh2 zL?5JZog(&A8a#z~w@_D^%H~ze8n%8qy~Hx#pC&)CEzPF$GY3WA_WTtWQ25_RN8a1) z;zv8Pyi2x#*6vFp+@}+g`RjRM(Wb$ZZU8H&Vrb=jy#A7yUOg0MgZ8sh{B$U+kY-85hZe-m987-`iiZ==?R8Kd2R~oTpIdo+ep~Zg zU$@wrSxx!INYEv^wX0H3%jFUN^SMdn73SEyukfC2BoF%0;s5OtED8QVwx z5O<}w-7a}RzfgF7Y8run=o%W${r-zP4$JaCeec^Jl$bMt^CMD!1n>ZsQi?vF(%EoL zg9`si3EMfheaUekXpJn#m)Ldsr<%96m=zfGJD*ws`Ya=LHC{gky>jkUka&oufxUi< z{RQg%$9R{1d!uq^_-C|k8o^R-~2>1YnbOngHY)g76xYB3WVt($M89#g! z)27o^6b6yP1d98l)GVD`sE=XGxD_Z7ol15xOYf>2>hDH1T6JLxotYL7(+49!P#G43 zJKk~^aSb<}gxYT&XjkOdVsZ;C{{0YTtal#)q$o4EXqH71#9<&RgdvUk?fVN8NmJ$b z1)t4rHdv7p?JY!YEUdhe+0)--*S@n%-iV)hG<}eIOG(#m?fduXuWv}9%cuMLFNB1o ztl3@9V(FOO{0LJg3~Vp-KIMI%p28QWbIv9{nu{_xJOmV= zdPb$$L(c(8MwAantWX}@w)o&)q2s%>GC}@ zWF~+%qbegueEj~o2HhT~DneDYkVOM{{WBO;xP9N~$wB{KNmhN`NFk?0-=As20NKI6 zb_jr0LaUNt43}L?l6X|FRo>c68$9FsiW`GAw7u5*v=@Q}kWU`;|y+MTdw6Wyu16P*q~jpWwMgZ>V# z0S#Z4uoi}huk=$GEB4O*Zs}{dkGJ$HPgndr+K+^V_$5r~8TOH4>z-$a%sLIGvQgRM3iX;LwKtzVLEHB$lT%N=10T6eP2BW5Rr7yo9J8t%K$T;k z|2F?KStXA7eGX`mSU+?6`g?Qf&1g84_ms1nw;!QUN$s(F%MC3CH9z39FP8=AR~VnE z{aG#BIibB2wB}TyTgUg8v-N?GuK-26S77qaW)X7zljp0d`by~*r%ZToRAnPfD7JKz z8gm&6roIN?jaJRxT^n$_1)d3dx!u{WA{OgD($V(c#36TLNkK=VY`>PFxmNqhGq>94 z6KfztirE0jaV}u=dz89kT53{+H-I4-yEhu4K-0^_qUh!DRE5o+%2aGMY)#fM>nMF zt}RrkQ3oenZT7O)i+3g^=GlI>Yp0MZ`D?Wzf7uH+JUaw@8({Hl5nhv&oLp4d5LJEo zzmN09k$jHUeM9|%GwV{TZ#2_=N@aWJVqABIVf3`&4Px8RRBpIAK!L48xJ`A zif`Sl?U^>isuB?`mpT&2Uu!7kDxqR$SRt@W)LGx?UDLl_j&p#nWEJ3}FgmIsPh6za z8fhXq{w!kthGcJPu_Z5dlk!Jg)Roe(f2J!XK^Pwl|d3Dok%w=EM{NibT=t#fki&wWj-cnr5;2 zP-XKW3h9BmBD7{k@a_OU`F&+T^SvkBx_^E@PFy%)hX*dy;_p7NjJRQNDy-QDg={3N zYX4VG7K$wEZ`&bu3T-K7q!XIj->~NBr74ASc{5GTl4&y|dIX1*NA&dh!uDh<)kJQh zs(9jQaZo3~b?P>g8*eC)3M|vD-|Q|;IXg?&i`lxozGKG5e%*w$tGenlo#(qW_d&3V#;ebX{~22S?3PF-JFdN`Enx%1eo3d-U9GBEk8 z*Gsgjc3h!*Ezf@mL6@xDVr1rd+V-sjy)QR9?{NA9z5Z#+EQzh`NvdSJ5&k0e;IL+J zS7jGSNoT7L5kUnwEv)fWHWLb`K)z+HzlZOaPv5xcMuXx7aUTk*RC zwiw9ErRJnu)P(5-ND3bQ30-!*j4)g8bC)-xF%#1GedBEMjn2;iK1%Xe*)HEzt_!(~SGov&$*aFE$?}ye%h@rK5f3=n@y56(zFZ!t ze4SeSqr1qQ2kj$8c}^Z%vAkbA*E}g|GT7Nq-Fzxf+?y0q} z48Q=`@rf;ohUqX2t5(l*Vf>t@%{yx9Ig&kY{nmAtiCT12c6#C#b5PVGHDESVd15O3 z%^JXBsJ-;xd|nP)q5gJXuzmuH97%_07`mIwx#Z}z*-VGVu6bB?Quz4!oj+bx>!hPQ za{s&NcIDF50h0H-00F>VzaAD9FdaZYGN^t|^fms95pO-ca5F+TE#G6esU&e`Xy~W+kkliZ18Aj7 z04oigBO2NUh~hXhn6kyzBOH2ILCkq57GUX_9YbVHrqe;`LQuncp&xnd>jCDdsVS_?2~~n1K#Jy z+oZmiQH5^bA+C^u$>Ng=KtU;r7n>2-WJ#04xHjJSb0^w^*PTc!ei<9mZ9Nn%reRyt zJ*VfXnS8_Oyy8Q;iYPH`5Bq{mQs>~`^A6H#P=c)}yOBa8fTBLO#I8S#{YW{=fx9qh zS!9AVVNEY8kO5zKcYLQ>N4WpxH}3a)KzW_w(;WH$2zqx^c2T={_(WLqp9VR^Llp4E z!B{IW&-fsPLuqJM8Io^8oyE|j2V?nRi+DinsQemsB~&n*8@CnpT$NqZyR`o@k!zor zdUdoe9ws8sWu1p|j^{*(BCm$)y6Ypt3;roXXD1qZvvy;yg@=c|*>I4)1SQC(<;Y%kLh}3Y-I!GH}50 zaNs!^l{e3B*>uMKO8Pp+i583Z;YI*>;F4VU2$R4uC$0L4yAw;ziy_{m73cO`eq_c} z_2;4i&ie4eTZ20c_@p^i;ji>yvQKnM%ch_BaxR=g>J1km!~l8^n1)`z_LIaF>*`GM zZeE8nqF!($5qgxC{Z&=*#6KJTxd^ydLlndy^u7WsAPlZ)@76gnV{W;JdnTq807zKkf;sGP9idZ=r!wLs6-=a*(VXS9<4IGYJH*BOai5 zdoXzyUZEYQl@Bb3%DGBlP-_qHGnxfSoU&@}X!9=K<%Tne`mvzuOyq9f59P+Y6uMxK zt?oJYC9C$Y>-o>~W%ns1^-aFMp8t(%s%JYXa>_ifq`wOezm!c*%IUl5;T_zZFUx%B zoA1jMQQ3BW@bNaJw~Dv#0tfkgVsyZ-=^e-dnbNl|6!h^ygDUZ+39lVS9gG^8^S-xv z!<>k4mBI8uz%s+H%40M-zus;bas`L2^|!=oyW;cr_vMbHlsaD-1=v=s-qk*=Wx!!d zSsHD`R?tsu(LcPK_ne!jTf``>$P8{darv-UDI%);$A(||EaE$9noC|%TTm=} z?S1P1u`6?OHm;+V^mO-^vL(&KwXwtcQiPzu-wfz)w z@dsLg%{gUtz~CaoilRlpXGF9JGgV~zr&6)?ut->NB9kf+9>M@+^xsA3K7x{sE_Txi&Q&vxkqK|9P5amM9LAJ(!)P`3zM}; zAKY;&QZs38O|u%mTXW27)`^dJ0e3;i-D#2la=J|!^+scqtA9x+_GmkkD$M!`=mKt) zN#};}?gxO-EDGFDW>yb-4vHFGOs9|pX0Bc+bib4L-0LS2oP5K?V+c2BJ9QrqV2gqH zm3y<9?pTb`SBq zKvx`c>fYd)y=;j7nx~_^Q(W{Q*th}Rwir*vYUMP8FfoXgJ%_u(WcsE&2})3uu9gf& zTs@RqPOxQw(6rKg|LGsqnU5R1l{yLRMS(|L)vMf?C2e<-Y}04-Z%(rY<6#m-M&jSJ zqsq(ZsLtlDmC9Ybfnan*;y}i>^G58DM*p*W8rSS3a#H)NtIcNpl!i+D!t8&l9QBKM z>jLvZlX1Wfm*h-6j`>;jX_m4t#uv*TAAv|GEZW8M3x?p+Klk(7;`uQHg`I0=Y`~g} z>9j9i+ru0IZ~BQU0JhuK3hs~46*iEFqia9|(N$(hP0PBC1JOvUgvZFCbF<#rl>x@fLsk&{Ur?0v#Kfk~bxUgGZbqgY=aBcr81Udg zr$CSOY)2KBVc+=jHG<-=eZBePYR~9I5K!N;Xcmqi`cq$GBBJdmR}(5D>}|J+0zBp( zoHeb^b{9{#VK0TmVpa#uEj8zG_ES_*X)@v%`r0o+G`^~m(bY7&GJ}$M0wLD zUf+mCw8ZMwHr|;`Se{bv9gyF}b6a=h+{ZlyuSM%i6uY423k?U*L>8oMb^IoS_WB6L z+kU|$o*zc!0`}&jqpB$jLpuuWyw3vV&+WS17(%HUh42t)Au2}j^Wu+zq%k_Z`&z}~ zM**Q10PsAavxm(s>Rj@azi0sSn~{YAg`b?q;nRu2>;NIblwVd(47F!w%S0er4E23{ z!^PzRlAx=^tv-J=*x(NT%a7~9$1sN|eTe^Fngk4kyC^_IZ|q)dvlJJotZura@B6JJ z1C?Jgq3PE9(QchxS4-GkF+&5K{8yhLQL?6gM%v|cep3+6+Z7Sd{~FA$9<%~JLAWp| zXn$<@X8Z|+{X2+?x?w5tK1EwVfR4Md%I|$RXFvU`UbRUS5J0IDOpWJzP-fP3 zcJoH?!zkcUJy+WJ(4mf{k=bhQln40)gbKWTfRBl@bX<$u?%Iuu5F6SfS;-tTXLZU( ze+gVs-PTtzeSLh)UPD9lFG5VU+}qmzo}gx*kTZ(&u>BOmaOj7qM0E*_Rr;>&)4WFM z%TR*%ml*O2z?uWZbFCVosXeodHi&}wF5bh*Q%}1ESzzRqO6KEecVEjv!t~p6MK_ak z{K?nokNIU~|2)E3>T-U4zd0BCobcTGv5N4GjNYtUi$*B5x^s7tc1&~Zw9%Q9c~z#5 zO~{VgMF27+HbiA*{~k8|f&7IH2do-2E+uodDxTjw1ovGx>Wj0c+y}m{Hw*|QCE_fr z0bHpDN*lXM3{h_A$ zwUDwWrMa2E>h0L|GTFArsogcZcwS~{SbR$XFR$+g%M>;onCcl=wf&SW8AU&hi3|S{ z6T;_#Y&9eem;7Vo4}5`&hLBX| z-5{vTCkLH3wY_jlw8xZuROzthkDGAF{=vO$A33wDuJ;O(V-h1?OTEzRoJf7?l<*_> zbhKXB=6$+^U`&G$E9MLUcXtbsPoD<{C6P6vGy_H~fH9vKXWj}OU(vcHE^=tePSM*^Due>N$2Gfw+D>^sU+ZPgY zNk4PSs&vhD28h4Zcd9I)6l9)K(zTZ=% zQ0-k?|FmqDYRk;%VHKfqjATLD%hY^+GsVT`0=Tr%yS!Aos8Bm@PgP*ad)fz!VrRaf10fO~g-^+~4O=T4Rt~rdJsSG_kJU zhZ!;=Az;ZLa2Nv=av2%S?MWD&b<^D*tZf2h3Hv2};4NDf5yhCa zpikT*O5M4U5WI%>^)vS^3lq8dK1u0F4_`5xAq6db_Dlg))eB0-;=j-^V`p+UE5ks+r6e>J1%uF3;s0w*Y|* zek{(=P@Eo=sm1#79s{i&elWQ`-=wle_fo{>EDDxJ_e9{XF%kPK+}A665^Wp3`=cT*!L*MAbY>523oJOO4t#4lM2I8Yi2} z^jAA)4;J>6BZI%XVXYYOm91-8W`{xFfK2O*45pVpazjQi`;{MZe(c>+CX#$ac$y#i zk2TO{7(nZ#{$dYq#v+%rZ{gVA2L3BE#dFRY3u&Gu42!HL_^84Wqx~I}tXMf`JWwLkUIQ!SvtU*V+Tsp6YyMZq}vl_NH5rVJAwA^rH zZp|u?!~sv}e-sFIxI8K-y8_ND`$x&1w8qq(+DpY=*K=i6v-j^^_h?beWyrjSq8mYw?!_3p_i zkNJ~6;S_TT8W?jgCl73E8{jZLr3$Im2`Ts=OIIGq^#A|+b{El&TwM}{+&7<6$z9Ie zMebwndy`K^a+PcDeUu}FoSSn>EDg)K*cj$!8=D!n@q14{e|zu!dcWSU_w)69J)e)m zF*h{YcplQJyf1wu`fiPm?djMUYu!hH3%zN;0gt!01V}!vw0TJLM=jN!^XCd;|{@Mi3fhd-xp14~cT)-Asx> z8l0y&W)0K=!(5jDUTd|PY3rkuJ3bsZWcWNi28}E_*;uaeIU%q4)#ir|Fg{e}nCVO@ zY+fWL65-eyDcjQ~2M6n3)$oh!(Cke*0C8>tZ={-V8RYH5^;?eyXp_8gx=^gAtPa-J^RcSFxb>X;RxzOo0+F{PmY$+dRo;$xjgcuk(0utMrkfG`R<<-bl>h=H0pr`X*%PXyJY62!;1$)$pQ#ItHU$m zbD78K-b_ZzK>!rs2Ja?|;-KnB$*@32T3vNoOyps5o!f7)BWJ=KtuzJb7@!;0Vw)KT z7D6C1Znu|juH7)DS~iV*015Wqe@8bu5~EHDm7uZz31ipg_vKIq6IiX3Re-CUy*yOOJ^6)d90eS`IV3{v8+V@CY9H@pKARE&7*j|EKkYirV1cS; zZcPQ~FS_;QWmM}|b8JR%J7rnVb=j2@k`An4Jc;zP)b>@&PaW{2DJwLVSX*wH8EvO( z)UM_yO8+d(T|nzR#E7V01o%M{wPtgKm4bc7n8v?ncOQywcH#`Qiy{lM^zKFHY0ES_RbfS;G*KnLY93=3wZpI!psQSrzvf42yz?vQ z)sqr9C)VtHV|4nnbB?_rl)Bfh^z1T_SPhzCNvn-_j#?kVLa}#eSwcVC0Z8R?P5T$v zR4&s2pkjRgs3t%M4{lu1$V8(KGL&wLUY{bl1Hv8I-7lslU4PZ-{F)6B0iGfg43zaWat`ao8t?=no0frO9gwlYQa7 z_doaLVd04X{WgSps>b+?);g}v+${^f!-mv+G>BpTXWViPSn#;RtfJu))c=s1pVPyd zdGNF=k}i2F3nQ9_Q+j1`1^g9^p8Hmb+E4N2E}HF~ z#oV8e`B=vaX~OLS>MwcDWEWV#2+4`&RugGpzl6S8Tm!%$t3=3Sm#=CY#Vj0TNKy(; z;N$XVbi8Lf50k^41NuUCh=8h2pFy)R*=?CKxd-r8#u}dpJH_iSCM9%XOl&^?0vXjwvI|6HETRn)O<<@*ye|<_TqnRi-x9jw1HI-_Of~Bv~Kxf|MoDp zmi|8nFg#Mku3C<4C@qwS-zy~^V@!8wYR?2eorVBLQ-QlZ<=82BBzYXqsjupj zeNZ9hk4fb&eU4&% zpS2WkQ$>KXxPzY~7`0~V+U?wQ!%v%J0|7{V<>m|QcSnxh+Mly6QyhJ7@}pNc_dLXK z2qZIcKU-8Yfil_FCd&;7LMLN$k=3A;JktDU`>9>kZXk~eyA5z|%ZhgU0V4Ej?I5R) z3EJ=TLcjUeZ4ztEKMzj&s!)Ahdx4r! zB1pQvSv^L*e#YQN5_!b1wY`$q=uQI*5;D;m>#a$Y$%)2l&gr%T`gT@r=PM<7pn@I1 z=$1>3Tsq&BYuF5gz22s=p}?R_%EymgSCm#g0()gc_3h2l39YPW%G=U6Wj^@ki1hVV zEV^}<2T}!}gCPj_aAitlYnS~AeBo2?87p0Xn%skB|GfZs%I;De;*B)`IO=N>B`X9E z%4AdFI~LlmoRu(zS{BLAImBQwF!TInD>O(OPMVkbfF3py`u0a_(kh=sm42FiQM
    6XR-t8ZI9^oC#D7rfW)F#t#eeDqQ3#Uf zpkX?AX?l`}bk<(|6Eli@A88-`Us~jlEA-*WkWZtQAc9e52^>|1fL=a%t?RvuJ7O||F8duq1ub9R zJ218}pY<>{+!j@p@x*If{eJ_UZP~C}!_MgXk(`r&=-D3$ktzB6Sq0RMt=fq3Rjlj) z#=G5y4_IbRz1H6NBl~9=FQZ^%Xu%=v2NP*PEODv@F&{OjmG1*5I^2LpEnpT~A43pD z9p4v%0`|t5Utf*Qioqr8@je=kg)(lS#ICm{!Es8BO z{U*kQCH_FzNWOyAwaWOxxPPsHfy$d(DD8@f`+nf*z>I1k!te%dMxR&i?08recxycR z+;TfI%bzh2rHG7J^v|YEc7Q+bPPukGa~?0Cd}=ueKDj3ldW*)6g+uYvnuMz(3!Hkw zRBruqBPIhV5khW$u?AjUmurKH5ES#lurGMoTfX7=R6)oSRo<@!rF$9_Y~R0uSA^H< zdZRN9AQNI(x776^__(S#LEZ<>kY=`P0-ivFN+~;Eq2`*=ZakAd$e|nU8Y|WNeRX#L zb@1V$6b~x+-GG^#gRA9u-mQNtM*{e!Bg5T3^OTyJ;ar9QqE+;`P<7wWm%2)V2snY? zC&0d4;6`lniBCbhhTRi@z37T?4D*J|O4`H-FLS`v(GS9r z94O72*L~@WY=T*Dx_Ay!m0!}rcfB5`dph$KeZn97M`nCU+RstQ^l78&yX+~qT)jNp zkJf}#gI&lpR95V;XdHb3I45GZcc0}3&WMJdtF{Zq*+>GIkZqhIo5N{+fFDo1IM2hO ze;z<~0{jtX!}xzv7eOW09!bVTo|xKY68y)E_pZsBHBi81)NdiL7wt+~4!~8X^v;Zc z@v|p(o*WC=q#_ziH(LPoezQ5sFYE&*6Axl%@<*JG;{6r2F6oDHW4)l|fGRg16^OvM zF947=ERbLXx$T88Xmww3XAk|W=*WX=klkOK?H0d$ua+#;=7RdY^ckDcJ%BupTD3+W z#r52XXa<*do?L2Aww(N7eJ?+U{``b*@mjPqh~{sr-jntjCd}XVe_r~wDIXyhQ!TKY zO+u{_Bgs~P9WB||cQPStOk@vWp$F#|@p_DLkeQsPW+}-65^i(=g-F~oNtkW#{21}Y zOWWQh5PXaVaUDT6NYy+u-}?Pcb;%gFY&lUHIdL>`G;5x>lOS}^;eviZc{9IshP?ms z$o3Zd5M#X|qQ+di9042q(PZ=o{UFjHc9J}9k%6PBOtz3&A+VggeXZ|m$3}F^G|K^b zEy5i^$bnaXi4xJ@%%?=2*ci`Uy;~8`adyNfnbh0v`ZkXY6i$U``P>e&*-9m?0a=U_ zcmgFBFbHnC-_Yk~rWz_u7Dhl578EizYp%KE4{2& z`OthL4c}dF&BD*^e|^aBK)rKGZo_q^9_#t;kCwI{=I-Qub1T!Cb#Ga=rASZu_yvg&;LbO=vXF<7!cL=N2 za?%s-V=fO055zaeXW>NZck3*xUYF@$g{N@`8C}x|Err|x$miVn4dIM}+ijdu=n#1O z91Sb`HL|Xm7c9&fUk6FaK9ugiRdBFYslL42;wlHvGGPiA^BOapDtOhp{itU0tA_3r zgx4p__SgN#I0=sc3Abz5rjwO-&D;(Sd0i43_W0Fg&u`a8G+5;M1u+6kKLiEa%GI1A zi3EchGE7oBR1!d5#zL>NPq}-|^FB-d+HxV^yXQxqWwbG9$L8iKC9fzT;8sUFI;6<7 zkcMcD*#yH@X3Pj@-2ewCb*;z~{tiw*HhYRd4TdA8o9Sr71}$D2DJ7iwM|s!ftn5IN zT-^pMD6+6(epH#?7a1XQlNp?I zCKS~~7~2iOK9g_GdG)aQ7oEq_@e-+b(>>9|CA6a@IDdE*Ug{Ve$*Pda0~C!jlLEH) zr5s)sO20^{y*y>T)rYHsJpsZDad zIjXdBta?!)oszsaHO`MOD*J^CFdN^H-QV%RmZE~||0PwKP89@JYR0HLS(cnuTU`;gHswnDQ;JA&|!@fqv z3YtT*VQ?Of7+tIPX#;8hqEU;pF!m4{UkybD!hFrMQzW171a6!InA+6=6uPt@NZe^R zfRXucrRhRk&Ivs3IOs+vX=UL*zZ?J4@W~7h-iV@uKdB80AH(}h=KN|3^rv*NFTw;6 zG?6~2PZTOMrgi6AJ9P|0jF^jGrWlv(x0N5`q!@_)&aTayz`8=HNR)0M?)i0?;L8DsA8K zGu-rEVfan_}+qLh>3mIl3>d2(YNw*win-?1vl#$1*^r$&FXW!jQ7viT=b3^qm)}`o=|Ol(ugoXDs64p^ z&h9vMtY^sq@{quU0eUzgGHveIT!8zJiM|-;+Kfps%g+jzPJfNDV9C^`RN%*$TeR9^3G3|_=4^Tl zlf>YzvR8z83JU*0FOukQ9r{j?nwycdOmWWG_dt}%4`yzScU~4)haJ7nBVpS%qv+!PucH^ zkFb%I@;2TW;`@hP=%3`!log$1o@?Uyx2s8U&(^yyXAsh4_@iAv^P^%qTBrw%sfjKwi-;SdvDfX2%eDy*IaM|7z zxUzKm@#eJzP3=uwPQgYgnqcxTm3Ej_78SK79m5>LPLB~Sj*uPis-m|u-I^Ez$}0Jn zIFCCy88Tr?f56>ed1ZKJ4Pk7?GfD|TStdQNjq3dY19Vy5du568Wj_Am^|&SJ?CFCH z%1Y7EorVgRoJLIXh^Pp{#FwS9?wmYiaw*M#@lf%cqwg>6*8t+dXzy?)(Dt@(e%6Q% z@-*J_7`70Z$(*kLyES%5ilko(c8)zM-xOY@oFtT>#QsTr(JhH{Z)xKnCHln1@ zj+I~4v2rR$8R!o>Z)8{aOMA)y0}H`*m9VBtCm$ z!_suS5+RXh&!7RGT^ggbq( zWIe52-=U2Nb)?Ggj2?nJzjAGMC!WP4mY-#gLc=!)UDt@O+k9d-S1M)yiM3v|aLGm;h`h;U9&8B0?&?)LBQ;?S1y%uk6j%$9cAJsx6PNHKIP>J0~$1E&H|p zD7iD$qUu1}En&hunRt+4`lbo=ep2`z2C{4PLpGe4(rvUn5(aLL@bq1t9vQ;ZL(rGm z9^Ce&+l4!r4BAS2Mbx2dq7`9YsRg{CB%gaM=;C*V$CMU9&mlD>9KIw?K-P|C2=6|c z`#0QNZ9n`SThZxE<9BZmO5j?^a}PBGc9~(VjRzNE|K%pyq8f7)pypa1#E>ALQUVUr-+o=8m})LA$LWsLPjA0Wyr-r z&EShZF!AxB1lsL!G7d%`jXqUz#SK8((a_XlMB}KD`hEqLFmlzi)})qL?HQ?7)b?-| z6Ik=-0opK5;SO8~u2Sa|ioZG}q|-?={{Zsh9s=ZkU^lAjxduX_4_%{dd+-#bk+WiD z2F8MOwV7{r(V?ZV@%5(0(FWo6B8|9h-_Ty|@E(`3xO-0n_YRvfgU|b=6bzyC2z?4j zKnx>|bdhAnN{Ew3`aF=~eE(Vx^rCiBzukjb`E4Eo$5#!|+p^zag0fST&@N0_?Bplasqh1hZ7TR=a;UM-K?Kts1`yU6}c zVTtrQ!drKnzm1sGBP zd{*Fy$N8snKcZe&pBk^EO*Ai*%>=6k3OS=T18TnxU7bSbe%7o%TG^QfljD9uUAp1< zLN&V`g}FTW4a$obF9jjKW2*j4i(evI*xyG}2R_1=9w-u`eTN+P#*WcxZ{1a;|$B=73SzB)&qn+cZ_4jGN<9vq27cOhUS8~uVCVz#?LxUgRckad{nlE zARUo+sq}(=)1R`+d0e~3z|L0oFz(E?kd=aCQ0(0K+6~K;&7PpM^u|rTduwMaL%jML zD9Xb}{uBGws?heJc6%bRba(c4)De1bqn8Zwwp&vkVY;_*DBpJ8m0lUe#&TWDgyqd# zDZ%{8&pN9ISXVT;Xx@inn_D|E>)?~mIiV9!Iz{u>?-JfTu5MLN^u)-wIE$oMm;C&? zyPNQS#XHRPOYTnZo5UeP*wiu*Oy9n3ZCCVqi_!nzoVKbVNY8gk5~Ls=M7ZGx(;ErA z^e2^^R`36%Q8xKt!MT-9O8;S~)yR1&NUh1m)m1XwWd!zCe*0W;DC&MKo#2w5_N=X| zLP~~3T z@m?+R}uK2sj?)7#4p?%%aC}0!(g&#~CyrRA;TwGqsw*w~T0Hh7_hly2u_}OjU zUpbwrfiC#+v~xxJmUgbM2t++dE_x%DDFBYE<3e4AP@90oIHqJGkPIY$T-y)+rbMm( z2sS%Bbnjk7qv9sC9_C<=MoFBx+<#={jHkA`A2Qtq{xvekyuWh9##nj625{>Tqbs9O zX}PBlhPEec2;csMWfqcm^Et1n153{Ya55i|w3tWxjrA=WbChJY;g1v*AQ^@kxDrRYdrRB)EjPLpK`n5ye(jd_t7ZY zB$dGOJ@woSBzx@vxDdmQkNbx8(UE$pt(ohyt zx}>->F#9n5!Uek)>xi4${Ic;VGC%=2rWbC5|Exp zT);|~){J=yV2VFuJ=@1|!AvUQN0Eq4*Cry|T8Vwi_c`aSuR#D=8UMs2eSz09N>uQZ zf9U`&NskteA~SgRHbd>L*LtWPQf5n+aB1u z`~>Q1?bXULF9(oZLRU#PLX&Qn!=;{>IyL0sHm~UckvnY;9A%kmcMX57Y;}nCJmM&19=LhEBQa}zR#T9-r>Do)v=K5SB(T``)Icna02{PxaIbT@)X`UjeB*yrAs3^ zih)i{M5~OzXo&Ek!D|w>-ivoOPDg01ltSj`;M)O48 zICk5kF5<0?;Qkwk&?j$xwQa~YJ>rz%HDEX?nT_T9dg&n;Y zYI-Qu1~nSiJi3Jbmeludg<|}-KtF|B26U4TAdOUW@{W?xw@x|Hn_sxoVw#>G&Z!vy z@c6#k=#6ux{dK$+rRxs&)E`4jnp${Fw+V87vZR@{FFSX+H+XL1M+gGS# z(oSf4(oSG{lFr(-c@z;jCE~HlAFJiuRlVAGh;d7H=kfi|h;gbyMOz!rcBHkX`U=f_ zYEpVDqOhPH$1M|>kyX=a06f^fjQkYVR>)I$Gq|7baGndInfA1`=(S6Z5oY01{Ux%Z z$!hH|uOa`n--$^8{B-Y1sa)AHq)E2P@S$Lc=d$0b-qd9B@{8xu?^^iTgC({^@@4H( zob9Rj%{zf|01wG0yuBRfQ#2-$TIIjDM5W6QeF^(0%Xx9E)4{VI%8hL-sS{KFVE_8a zFIl2*a`woLp$u)YVsKJb5jTutbckVj#$|rx_vErtQ20wY22vyRh z{RnxVIenofR;gyhtYVIgsM0Na=qHyU zU-m={^b6zL`@*?x@N`8nPgCoAJqgrn-8Ak8`Qurs#DhEJzU@JbBDtG31d(i3v0Em+DT-pSL#62aZ zL}*qI%N##Td|2suZHmecl$0tBR(cjeRI0oh3rgTg`lx!@f_=^=POf+^&>0e5eB~nS z1%gzV?=#I$=P|=Vb|(rK(j=cY$>#L{%P4qH;Q z+ScLfZbtFB51`=;qNvQ!UC5u}JtcokH5*rk#^&O^tNQmf_WVA+DTpIYtg%Kez2y1k zA8u`%S;!D$uZ}i9z*!pVurAqJQ)2yR>^Ie1qddCs?V`rJSj;r`5O(sE>TNQpH?Pw4`yv*SDQL%oFO3gD!bifVv@|o=8wmVxphE1t z)Gwq$zR+G;C`|!NqSXBc?)=uN3$J+*R5G>J3pe=d5M zSN0aTek|y>Yeku&UFK_|Pbqs>;iTV|ZnqnX@M_n8SpC42Cf!65O_*mcEZR_3?&h6^BDk8euwf5%{ z0+dXzWKn0Az5wP^8-8*JcZTQx#EJu_RmC^lMD+3YPhjkp=0S$+Otm~9pjKM6pl8sF z^V<#5OHYX261Z<~YN@xN-0y003u%kwf>%NY=jCUs|7m~zeosqFNQAiiZHZ4+5ltZ{ zFXi&fr!Ccma5O1i62zd;F%nd)GeRv>@=9dB&6da^bKC#T^?YwOU0hbdYBbNg18x%D z%Hlu8y-qb2dxo6KxpvX8qO@U|DmND#C)R*DD+KMg&Gj-xkKPCdlpazlhoN)C#DUqh z{}JwM={GIB?HW3kG8L&}7tU}Bblcv#jEszswt;ndPfgZhB>~gB=<>P_uu5fuPF~Km zC#F;d_61=RTj$s=HwVJvMn+7=fq@=40IHhv$CzGQlIw0Aj)_0Bt{ReJ_%$QA>VE{E z=4Fh%%GOEnPsk+%Z&}dm31`5q{B`pb80bJ2h+xG+0Eva>Lu`R1Mk9Ih{m+F9;%5Lg zC`hCJA)6Aih73X2obZQgky9TJt+- z^YXsWA0#%_G-I6n;o~S``O~%Xyt)OdjOwIR3?)Ri9iKT32iYdfXE(CLwv{ptT@cvV zc^|mo|2*T`s&9`v0IFrgt_5+A8C=%N?|uZHpXedyA0g9(leg7jGq?RjPkw=$nNiXc z4VERhmy^Vn&R(j{&b8F`34DbJbGo&CHfhDL1}76zRuLosXxl_nM-&}kg{#kZ1aHl& zANFt1cK*bft`5GTt`A-O`Me+I^7R{*@tO)nsr-0dW~eRbRd$DF zc2L-+G|cxW0rtE_q<>ec0{P+`P}~GJyAQqV1X*5eK?NsIK3|Eh4`mC%ommy#>TdUh9@93 z0wT#fcz$wAf{tujIdt%r_9oBa)+W+befbB^Zyox>x$N)XOBo6ML1tlxj3SFV@6XQ0 z818?EaD{G%G!HnYA!}Or1U~#12d4Y!qGi?F;+OlIqV_!v+8yoVvNKzA;P5e%>aee# zHf${;`bf6VhEUpnq|*PF!X==UQg}%v6I@sO*Y(Py_pq9#!K}#jZ{=soyCo}FUj|Tf zY*-iIHwP5D*gE~c`C9%|(BFvHPLeiPbjI8j9<@~xmlDp9QBfKL3gC`mlkv{Hp9G5N zxNU+CWY#0Rdnti(Y^^(1{pOOn)~@C%`awp??sokWexxq5I{V8Pl_rX5P6PVCl`Hl~ zC(u>!<_LI^(cUsm0|g&XZKnlj!VBUsa>j1z=Lq?mMH-wnf%(4_pBQX42InxID@6opavFubd()wT+94F)SGAcdx<^aoyfe@)^qXLBp}^nHThSYkNpx=Sn2q z`F?qU)>4mDGqo;DLmU6u>(n5UON_(xIWYBa>MJb1^2TZ$u~rb?{`L{LrIjCnT|{!f z^!YSSoZC5Fwj=kk+1>2yHeg8k@RaUOb|YO!z7HgppD+DY>qO2FI!JR?sk4b|;5LGoIP7-Fu>z(aG!lP|EHIKd7W3xl;qKgGj zAcukGik4%YTY1Sy*irZ?)e<`pB4zq@d4Y2#YLnD~+g>3i-{;*;JkqUEai?- zJ@KEUxX*Uah@HWh`g%1KYW>T^*~2LVAvppk8KFrF@*6YDX#a`l+esC?^6P~qrvlOpG~)3C;j7_ zre)71iaB})>q*rSuRhb`&86(@Sz5xS8FzSdzWWS?$G}Na^m!vj`J&Z>7ON+rp9+5e ziBR=z>I+-^IQA9S$_7`W{6MYr+R zVgSIO;uiL3@9i%~Z9~pi^iq5KZdqr&H*p4=3-FP*{I(Vg*OiyfE?nUj?%qq5T>;OI zclN^+pEjIT23}Q)ULr4qyQIbZj<9#qcsHH4$6ZgSIh z5X7(STUt`!ZJgL}{#Y>K?)3rsQ~%ucvutVo9c^x_l;}&OJ;R*sx9I&|(iXrAi4452 zq7kV0eX;gM<6Ul_Dg)}qc-V$htV2MrT%TfC?YiNV7H$>TPGBVF5wdbQ)B(EXs*}@S zhqXxm5bq|~-u&VumOjR~Ll}9A3GQeyoq#KAoxsZ|y}}ys+3r@GiVom++w3YH?yGdP zHeD1#xi6X)x||hb80@qh8F*HMBe2J$nHKH&5DaRYs^=_;X%iCFRO9AHjw0&gw|x)n z7Q6Z-kair)26wmoRH}yPw4G!7g5(`Cb?^MiXNKqhBa!01lqb)<%!v@(i7+}lH zv{Ol)=?gg%z7yh3Tfl_W2pn^lapH$~j#YBf0KneNH>2X8^#2h%dmo>ld37{#VWTy% z$=KFTN~xlD_Br1)6Q=R9twFC3Ai5~46uogi6r+y(y+z`OZ2WfZc1-$_@<0U2n6u(Z z;pNkRP+Vn7Y?+WHyN=jeW27{PcPmztSN{B#?e0PuG`MhXHzn`G7k5*=J-JzSP0AbF z+Tb`Ev{4q+nLhgEq|69`wD-*lk=Bx4ipxi?dO)SO*aW+UKVWL|f{%xW&=+FH4 z4fgl5S5eJNY965Ao&boUZ}zfJt_SbL_eRSvsq->EZ7Wd2RtC|q1P-|=jKMFR&Rcc@9X)5w**PqQoAsS1CflHcpO;88b+mNq5u=kDq z*$_lW;0+xdB%k!1Q$ih&rZ85-`wg`EKU{+KimwM~RDx$U`=LYTP{M=!bvaShFyRn; z=M64RyT#@4QC)CuW7qS*9+r)=z!0qy@&r{LSFZt*_1Qt(Jl50;1Jd5tOg?CPeAE3% zmhz)xb>@X*rJ(xU{SATTid+8{OPDE(&2<(r(M+$HEJ-;%QgJO>@vF$WS;u0bo>J*oX^$ePauHc6t*C8}TrD?I?j)v2zuA^i z`j)7m-Pq1l&zrhaz82LLEdS-G@r60*+NG#A67GA}uQ@Do6&tan)f&I`YuLs#4z=~+ z8%oPyUm7e71mG@0=gLv9*{gF3ok=H^RQ*!2({=!6Q9y&{FrlA55$5sz)r`H9qXyyj$4Ca>FI!a;2Y#idyg<+79ewvOAG%xfHTV>{_VG@dj&j zG%e;*|5lz}H<(J*p!$N6Xaglje(MR*E}jl$+FdeHqu4f%-%dMe+)7#M zXwOi zdIEeOYdw=Fm2XXqne1Gi>jXp~TC{|)CfEBvc=1|S-j0te4k>FpeMxnUf13f}_1>Yy#Hv=s3cPBqaq zMP~^B=3PXSBBK23B|_2izEg3O367sj^p$NOzw!sd#jDerH_OGqD5wMGLVOy5f@>$&HZVxha$$=(yf#PxFJuHRxCcxfeBz>Q)Hhx$+ZQDVPz zlTWf2>NHC$Hc7^&Uh@Th;d!XfUQ}5{#3Nec%-!13m`L9=FMe5Rm zM;Om?oM*6&jR%vdcL923SSw#uW`TOv|7O6;g3T)!Y}L*+BuT{0CW-l&%6P64ecMA+ zcWU=SR|hLD+CRTk<`MJakMD4;EdR0lec4pIxNLPF0BiDjlAK+^8HQi3g&7t}9$zP--Ko2_@cSaFb(CQYOwzEQG7_K|Y6^;%qZSkWEIkU3gQzqBa5~vb#+LnH7GT#v)?WUV?GffxV?`VV70@ zza2KD4KUDZWBG^;2TqA7dx(6B`Ab6zdy|t0PiJkt4_teov0BrZ_3on(n`j!yPO>T9 znT@G%3swcJ0&B*gM-wJ?$tE_p+61GC)+ODgw2W1j( zPIeJ#g1uY?v0hHq2)``JdHN`i3fAf2pT|Dk=)E$yY`3_rpOIzC8{z)m?N^$4=$T86 zo6bR#3-e;9s*QXv*hXD-%S&{tw(T_-jPz(x^;5pjKGkPF4s>7C`#V!hjR|>~?pSp( zVr35NpYg6Y3#0ws8&Os$G3)T`W*^4uUm>{CFjIlvg8THW2he!H#i!E12^5EJ0GnBg zd5TUh?}bEWT$%+rO0mtg!ffQWdFBuOd3Qg&iZjv!tm9{R+AjysC8!zHTobLHriSEG zh7t{c2Cm6$zx>6xfN%Bbt-+@M((CfTc3R}8P4+>yQx1pNeg61`!TnTgYCHB!t&2Xb zyEEYKuHM~zn<10p^DllnRZ(NS?^+qkS@Q$04hC z|7OOmDL&~%H{VDA`O+C(5iGzGnWdWvCVL`$A$M zp~`EW0m(>p-;=nzIdl4(udz~~x2BnE5$_PHc|`S0LGMPi*S(#4Qn6KY`hyVPPgQJw z1jxBPeBil8a%W$-H9r~$X>2P)O!!{m(3fd#taQDkojCqc0>Pq+% zBdz>@r#;&IeY~Lq&zoaWrCXz;oUtE}xp2ibp$oMgH?izn=jh0N)eH?6J|8v4hUABo zX|7OxL+whppCX!Zq-&r)n)nH3Mu#T-v51kiL*|^SgNdl;Q!k+gwvDQ~Z(NP{N8isS zYd$8ikw?RS#f2JvL*m=*rk$Bq}aUHBBX2rzy*j-D0n+m#x9wdX&5%=Hkg6 zq3TDFCMMr&wRIvlw)Xy5+g1xOpH<-fCub7qgV z=B*mvn)wlXglD$a7cPar09&i%4)DniB1h9C9D7uj*q|GOV)-jtr;fNeU2-VNhOM*r zk1CMQLLn44*m8E~(9?ElJvN)>mE$-F)rs#*dSG?b$HcQWx3pEzW5TkNy>e_{^Xq`N zA&*a7A=zulH1p+VWS4z?qAW;Fl9K7ySnYrDWKsQ8w| zegy2WGK+lz{dJ)@>_Kiy5Jbz%;YX)AyvaAxs5ek{g(UZVV^_AkLZ}+sQ2WR3@L0oD zmei3M?eX`m$d-kR_JE@H73^LUz2B(qd|)_CVefy6>w%S}aw8FP_Vm)7BZfobgngrn z16rnfk|?>+Igo^_CV1GB1*WYvYZxg63z~fGZ2(HA> zaBQ3Ezm?w1^4E2%i@piHYsarzne4eWH?d>zCQ2ahb7<_ZH#TJ6kyN?D3WpVfoN-cM{DT+pK*pS<{jAvlql98?y{oa{3URn3)7 zsjsB@Ba1Xt8&_1-5Z2=xax>IIT-4^!Mc!?wK(Nk}Pti3!&~FWcnu>;$yh-I>dJ%@= z=po&kNX*;&5O`aQ45+D>Gr%dncn#UqUxk@na|h)d`>y-RtVi63ci!3j`A?i!u|GEi zbj6$WMoy#k)R^zb64pm8V?c<@Ql*SC{~pYZZ*j#1_xJI|$?>-~utM#42BMNyz0mWj z4XbrgCUb~Ct7!Y;D?=voBxwV+C68*Gsoin$zJfTavLvAa7KqwshJ{tVCiwA>x zb^WaQx>;^V--_`;EcMax$IDh^yrnEFMCs51_Id?Pw#C9?v_-%i>!JdP#Z+?>0~Y5Y*^;}^Juv0eC>R(l7U)k!`Gg#Uw@1R@0K?^RYuB}LE5!u z?=AS>!G*0^mbFSpTZ?TlYhoiOBHE7zs7z`(w_op{G`fZD&;->eBzXO|`cD$I6z24& zbL)O(Y$So^gLT+Y)hJT%<-QMh354B@-rPBN`tCkGdrk6;N-}R6p2U}H5uP>M^L?Ck z&ZjYfsW4(CV{vdaM`l3-EEpgl%Uk%F^2(7Q8I|D{}x9D6e^wOTbU z`O3}e-d=KdT>3mJtpth=%UkLxZuo^!Nm?dtZ((0*HOR=ao%n{D3(7WpJseAFi$eBK zf_|TXcG3Vj?Wo5!vX3(~ zqV+_7Lnr#p%(Z2_ij9q9jRAW=!R=kaTZN@wTT!ng`u57sXqw9eil zD;;$v8Er+@IAHI4&ApjUw19?NGM-8i3~t-*HMqBWJ%6J@l%s z6j6BgSYx@7Y2}i+ zT}sOo8cogJv>C@Ox6EBrBSjFJnOp8=6+(p!K!41g;HwJJAMBpO+ z8Fzkpe|yh!&hx(Sd7pEh&+|Q{PH|qjkWUj(@9By5lCD+(|F*ek-2Ww9XG_R+HizyP z&F|Vmq@n;#lO?&k57{yU8E^iRvi6dEL@}wXx^uSJJ)J1zj#VFILHf8B6X#+i^@9$uAXFT0e6RcBIOc{$dB3Kjw2oOP@SYf~gG@I| z2#s$V71fcjbf>7oi7qu}L@Nv$w+jRHC)C-KNGk%Gw`pXyra*iiwO;vpMRFiEok-BP zn~laZ-Akb9B0qex`N~#7o#shup>wHsF>l9U#A7vR7`yl&I+p<~@l@a1bkGJbFA-(d zNTq6^CDoh>D=36c?+mKtz{txx#+|#(h7;sx8>ehU+Z8sl+Ey0B3)C3 z&wvnS`K7cGirf$d8wtB8i62`}xmZb~qFK_wZ2i;)_ngjjeeONqOLZ5jub?gEYh}m zNKuw;d?bv)Dc$QtV6`mgzhA%Y zYF#u2{7dDrJBt-yF|>XL^>q@bMBQcBi(xDSWOBdpBsg#|!8^{zS&{q5m|Zx)f}cd4WQr@gW899mTfaiJ87)#VVqxuysd+(_PoB(VbWCU;f8ykS>>BPxH->7K=_uUCtz;m0w^ z%e>Ck0%46B>!&_5;U`Im-yRd zP8(@tVdpd2X%EkY?gAv%-~{#bS5Uju_9a(OWQ_`@6fv6D<1R_L`RZ%XGc@DKu(ESUDc7E6P6{*ma++f<*Ok z1U1y;iJ_DD%$+B`CQKKM+t3p*`XPaoS;8O3s?iUr5m%m5m2rNnj>Ftc*e{MQ%d9& z;EUfU?CK7D1L%E%+q)Il53C?zlVKE7?(MFHc zF{UQE*JSK35uv?*_1+h-Ux;5D|#jC*N1nEB!lxAyx_LmEq3&)G98x% zJ{??g=%!+EOYeBxf$rs59}B_UzIaRNH83U^dG}%{$_Ix4RRDZ)4$XV zp^_pn1SBuELt|pOf9M3=U$2LlsU^???60h&=t|J+W$UrEn3GF0y zqR^dqtMR2xHhLR54hrBDjpKH_=oIv- zzXW^O?@uZ!@L40@v8yr%5cYv%_?vrz0a9F^$HmiaV`op33+wfB))ica*`BPqS`|1i z5AEa86-Rj@8S`poI7Rf7QIPDAZ<~!KL9Mm(8qR-Am)4{q-aWwZ#1cEm^QvSybhmeTO#QL$8ENz({W%7oIG_}!nnOnu zC2*LOef$080u3>|I@gsG$eyR2_>p7oAI#HrYIjJgu1l8}*=;aS{XpbxONt3vm6r~E zqzu;gfsJ9hg8A0CzvnxDZg8_oqdcE0Hn6)>(=-{*4pjzSR}7~02iVh1fsH2|3ftbN$=eqrvH#WO9Y>UGCbiS+MEp$h!Frihk|XpgIA z$9IjKoAcfpFEq1@vNs#WKicu;ndJr?cKd!d%*a`aEE~G48@VgAcv`AC1=r9}r z1GVo_KT?ly+22pKWyXo#&#dT{L_Oc!0PzPcmhW=;VoL1dWODaVQ*{DMmpMX$Nu2qO z5n3oNel%&_y)3suF$Ne2G4P3B@H$Tb%Cn6*JKS7 zE6<|ubt)~{q9wLl^HTrKV30FAXbSD9JZX{H-}THA^ZvEij@_diwf@SOPo~l!+Ho!) z94A?3iZOSdVNtPTn!1D)l!6;WuX760sd|1K#s;gv%0c8c_;qodyEe8IW@J3+H$B_u zYNI+t3yRc1<}Q^!K!n7@uBErt_m&W6|MHY{l^a~)qGnc(xdER#Ot$zju|`5xOL3dl zxohz*VJX}wdmeR9)lTdK<4|6Fc7(Qk$Tmqt<$^PuMQ5(7dxt@S_ z$0=P7;hm!*9I#>`fqH0KT#9^f7n?()F12`|g?^=ALv8?%k2;|;92@1Oqzc8>)u literal 0 HcmV?d00001 diff --git a/TagsCloud/images/taras.png b/TagsCloud/images/taras.png new file mode 100644 index 0000000000000000000000000000000000000000..5aff207d98c27b3dcc8cdacd6a0381fb883549fd GIT binary patch literal 91006 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 Date: Fri, 26 Jan 2024 21:10:46 +0400 Subject: [PATCH 08/10] update --- .../{ => App}/Actions/CircleCloudAction.cs | 12 +++--- .../{ => App}/Actions/FlowerCloudAction.cs | 11 +++--- .../{ => App}/Actions/ImageSettingsAction.cs | 4 +- TagsCloud/{ => App}/Actions/SaveFileAction.cs | 2 +- .../{ => App}/Actions/TagSettingsAction.cs | 4 +- .../{ => App}/Actions/UploadFileAction.cs | 8 ++-- .../Actions/WordAnalyzerSettingsAction.cs | 3 +- TagsCloud/{ => App}/CloudForm.Designer.cs | 0 TagsCloud/{ => App}/CloudForm.cs | 7 ++-- .../{ => App}/Infrastructure/AppSettings.cs | 0 TagsCloud/App/Infrastructure/IImageHolder.cs | 13 +++++++ .../Infrastructure/PictureBoxImageHolder.cs | 15 ++++---- TagsCloud/App/Infrastructure/SettingsForm.cs | 37 ++++++++++++++++++ .../UiActions/EnumExtensions.cs | 17 +++++++++ .../App/Infrastructure/UiActions/IUiAction.cs | 9 +++++ .../Infrastructure/UiActions/MenuCategory.cs | 12 ++++++ .../UiActions/UiActionExtensions.cs | 32 ++++++++++++++++ TagsCloud/{ => App}/Settings/ImageSettings.cs | 2 +- TagsCloud/{ => App}/Settings/TagSettings.cs | 2 +- .../Settings/WordAnalyzerSettings.cs | 6 +-- TagsCloud/App/TagCloudPainter.cs | 35 +++++++++++++++++ TagsCloud/CloudLayouter/CloudLayouter.cs | 4 +- TagsCloud/CloudLayouter/FlowerSpiral.cs | 16 +++++--- TagsCloud/CloudLayouter/ISpiral.cs | 1 + TagsCloud/CloudLayouter/Spiral.cs | 12 ++++-- .../{CloudPainter => CloudVisualizer}/Tag.cs | 2 +- .../TagCloudVisualizer.cs} | 36 ++++++++---------- TagsCloud/FileReader/DocxParser.cs | 1 + TagsCloud/FileReader/IParser.cs | 2 - TagsCloud/FileReader/ParserHelper.cs | 6 +-- TagsCloud/FileReader/TxtParser.cs | 1 + TagsCloud/Folder.DotSettings | 4 +- TagsCloud/Infrastructure/IImageHolder.cs | 14 ------- TagsCloud/Infrastructure/SettingsForm.cs | 38 ------------------- .../UiActions/EnumExtensions.cs | 18 --------- .../Infrastructure/UiActions/IUiAction.cs | 10 ----- .../Infrastructure/UiActions/MenuCategory.cs | 16 -------- .../UiActions/UiActionExtensions.cs | 33 ---------------- TagsCloud/ProgramConstructor.cs | 19 ++++++++-- TagsCloud/TagsCloud.csproj | 25 ++++++------ TagsCloud/TagsCloud.csproj.DotSettings | 7 +++- TagsCloud/WordAnalyzer/WordAnalyzer.cs | 11 ++++-- TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs | 2 +- TagsCloudTests/AnalyzerTests.cs | 1 + 44 files changed, 279 insertions(+), 231 deletions(-) rename TagsCloud/{ => App}/Actions/CircleCloudAction.cs (75%) rename TagsCloud/{ => App}/Actions/FlowerCloudAction.cs (74%) rename TagsCloud/{ => App}/Actions/ImageSettingsAction.cs (92%) rename TagsCloud/{ => App}/Actions/SaveFileAction.cs (97%) rename TagsCloud/{ => App}/Actions/TagSettingsAction.cs (88%) rename TagsCloud/{ => App}/Actions/UploadFileAction.cs (91%) rename TagsCloud/{ => App}/Actions/WordAnalyzerSettingsAction.cs (90%) rename TagsCloud/{ => App}/CloudForm.Designer.cs (100%) rename TagsCloud/{ => App}/CloudForm.cs (87%) rename TagsCloud/{ => App}/Infrastructure/AppSettings.cs (100%) create mode 100644 TagsCloud/App/Infrastructure/IImageHolder.cs rename TagsCloud/{ => App}/Infrastructure/PictureBoxImageHolder.cs (94%) create mode 100644 TagsCloud/App/Infrastructure/SettingsForm.cs create mode 100644 TagsCloud/App/Infrastructure/UiActions/EnumExtensions.cs create mode 100644 TagsCloud/App/Infrastructure/UiActions/IUiAction.cs create mode 100644 TagsCloud/App/Infrastructure/UiActions/MenuCategory.cs create mode 100644 TagsCloud/App/Infrastructure/UiActions/UiActionExtensions.cs rename TagsCloud/{ => App}/Settings/ImageSettings.cs (77%) rename TagsCloud/{ => App}/Settings/TagSettings.cs (85%) rename TagsCloud/{ => App}/Settings/WordAnalyzerSettings.cs (59%) create mode 100644 TagsCloud/App/TagCloudPainter.cs rename TagsCloud/{CloudPainter => CloudVisualizer}/Tag.cs (100%) rename TagsCloud/{CloudPainter/TagCloudPainter.cs => CloudVisualizer/TagCloudVisualizer.cs} (53%) delete mode 100644 TagsCloud/Infrastructure/IImageHolder.cs delete mode 100644 TagsCloud/Infrastructure/SettingsForm.cs delete mode 100644 TagsCloud/Infrastructure/UiActions/EnumExtensions.cs delete mode 100644 TagsCloud/Infrastructure/UiActions/IUiAction.cs delete mode 100644 TagsCloud/Infrastructure/UiActions/MenuCategory.cs delete mode 100644 TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs diff --git a/TagsCloud/Actions/CircleCloudAction.cs b/TagsCloud/App/Actions/CircleCloudAction.cs similarity index 75% rename from TagsCloud/Actions/CircleCloudAction.cs rename to TagsCloud/App/Actions/CircleCloudAction.cs index 5d82a8bd2..fd551ddf0 100644 --- a/TagsCloud/Actions/CircleCloudAction.cs +++ b/TagsCloud/App/Actions/CircleCloudAction.cs @@ -1,23 +1,23 @@ -using System.Drawing; using TagsCloud.CloudLayouter; -using TagsCloud.CloudPainter; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class CircleCloudAction : IUiAction { private readonly IImageHolder imageHolder; private readonly TagCloudPainter painter; private readonly AppSettings settings; + private readonly Spiral spiral; public CircleCloudAction( - AppSettings appSettings, IImageHolder imageHolder, TagCloudPainter painter) + AppSettings appSettings, IImageHolder imageHolder, TagCloudPainter painter, Spiral spiral) { settings = appSettings; this.imageHolder = imageHolder; this.painter = painter; + this.spiral = spiral; } public MenuCategory Category => MenuCategory.Types; @@ -27,7 +27,7 @@ public CircleCloudAction( public void Perform() { if (settings.File == null) throw new Exception("сначала загрузи файл"); - var size = imageHolder.GetImageSize(); - painter.Paint(new Spiral(new Point(size.Width / 2, size.Height / 2)), settings.File.FullName); + + painter.Paint(settings.File.FullName, spiral); } } \ No newline at end of file diff --git a/TagsCloud/Actions/FlowerCloudAction.cs b/TagsCloud/App/Actions/FlowerCloudAction.cs similarity index 74% rename from TagsCloud/Actions/FlowerCloudAction.cs rename to TagsCloud/App/Actions/FlowerCloudAction.cs index 164f6d195..fc273c427 100644 --- a/TagsCloud/Actions/FlowerCloudAction.cs +++ b/TagsCloud/App/Actions/FlowerCloudAction.cs @@ -1,20 +1,20 @@ -using System.Drawing; using TagsCloud.CloudLayouter; -using TagsCloud.CloudPainter; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class FlowerCloudAction : IUiAction { + private readonly FlowerSpiral flowerSpiral; private readonly IImageHolder imageHolder; private readonly TagCloudPainter painter; private readonly AppSettings settings; public FlowerCloudAction( - IImageHolder imageHolder, TagCloudPainter painter, AppSettings settings) + IImageHolder imageHolder, TagCloudPainter painter, AppSettings settings, FlowerSpiral spiral) { + flowerSpiral = spiral; this.settings = settings; this.imageHolder = imageHolder; this.painter = painter; @@ -23,8 +23,7 @@ public FlowerCloudAction( public void Perform() { if (settings.File == null) throw new Exception("сначала загрузи файл"); - var size = imageHolder.GetImageSize(); - painter.Paint(new FlowerSpiral(new Point(size.Width / 2, size.Height / 2), 0.5, 4), settings.File.FullName); + painter.Paint(settings.File.FullName, flowerSpiral); } public MenuCategory Category => MenuCategory.Types; diff --git a/TagsCloud/Actions/ImageSettingsAction.cs b/TagsCloud/App/Actions/ImageSettingsAction.cs similarity index 92% rename from TagsCloud/Actions/ImageSettingsAction.cs rename to TagsCloud/App/Actions/ImageSettingsAction.cs index a240113f5..fab317018 100644 --- a/TagsCloud/Actions/ImageSettingsAction.cs +++ b/TagsCloud/App/Actions/ImageSettingsAction.cs @@ -1,8 +1,8 @@ +using TagsCloud.App.Settings; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -using TagsCloud.Settings; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class ImageSettingsAction : IUiAction { diff --git a/TagsCloud/Actions/SaveFileAction.cs b/TagsCloud/App/Actions/SaveFileAction.cs similarity index 97% rename from TagsCloud/Actions/SaveFileAction.cs rename to TagsCloud/App/Actions/SaveFileAction.cs index 9656a0401..a3ca9ee6b 100644 --- a/TagsCloud/Actions/SaveFileAction.cs +++ b/TagsCloud/App/Actions/SaveFileAction.cs @@ -2,7 +2,7 @@ using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class SaveFileAction : IUiAction { diff --git a/TagsCloud/Actions/TagSettingsAction.cs b/TagsCloud/App/Actions/TagSettingsAction.cs similarity index 88% rename from TagsCloud/Actions/TagSettingsAction.cs rename to TagsCloud/App/Actions/TagSettingsAction.cs index c47abf512..96e4e7abf 100644 --- a/TagsCloud/Actions/TagSettingsAction.cs +++ b/TagsCloud/App/Actions/TagSettingsAction.cs @@ -1,8 +1,8 @@ +using TagsCloud.App.Settings; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -using TagsCloud.Settings; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class TagSettingsAction : IUiAction { diff --git a/TagsCloud/Actions/UploadFileAction.cs b/TagsCloud/App/Actions/UploadFileAction.cs similarity index 91% rename from TagsCloud/Actions/UploadFileAction.cs rename to TagsCloud/App/Actions/UploadFileAction.cs index 6f35a6ddf..da950e52e 100644 --- a/TagsCloud/Actions/UploadFileAction.cs +++ b/TagsCloud/App/Actions/UploadFileAction.cs @@ -2,7 +2,7 @@ using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class UploadFileAction : IUiAction { @@ -12,10 +12,11 @@ public UploadFileAction(AppSettings settings) { this.settings = settings; } + public MenuCategory Category => MenuCategory.File; public string Name => "Загрузить"; public string Description => ""; - + public void Perform() { var openFileDialog = new OpenFileDialog @@ -23,10 +24,9 @@ public void Perform() Title = "Выберите файл", Filter = "Текстовые файлы (*.txt)|*.txt|Документы (*.doc;*.docx)|*.doc;*.docx|Все файлы (*.*)|*.*", FilterIndex = 1, - RestoreDirectory = true, + RestoreDirectory = true }; if (openFileDialog.ShowDialog() != DialogResult.OK) return; settings.File = new FileInfo(openFileDialog.FileName); - } } \ No newline at end of file diff --git a/TagsCloud/Actions/WordAnalyzerSettingsAction.cs b/TagsCloud/App/Actions/WordAnalyzerSettingsAction.cs similarity index 90% rename from TagsCloud/Actions/WordAnalyzerSettingsAction.cs rename to TagsCloud/App/Actions/WordAnalyzerSettingsAction.cs index 1aca723bd..055839899 100644 --- a/TagsCloud/Actions/WordAnalyzerSettingsAction.cs +++ b/TagsCloud/App/Actions/WordAnalyzerSettingsAction.cs @@ -1,7 +1,8 @@ +using TagsCloud.App.Settings; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -namespace TagsCloud.Actions; +namespace TagsCloud.App.Actions; public class WordAnalyzerSettingsAction : IUiAction { diff --git a/TagsCloud/CloudForm.Designer.cs b/TagsCloud/App/CloudForm.Designer.cs similarity index 100% rename from TagsCloud/CloudForm.Designer.cs rename to TagsCloud/App/CloudForm.Designer.cs diff --git a/TagsCloud/CloudForm.cs b/TagsCloud/App/CloudForm.cs similarity index 87% rename from TagsCloud/CloudForm.cs rename to TagsCloud/App/CloudForm.cs index 4cdffeb35..c572f0d2a 100644 --- a/TagsCloud/CloudForm.cs +++ b/TagsCloud/App/CloudForm.cs @@ -1,8 +1,8 @@ using System.Drawing; using System.Windows.Forms; +using TagsCloud.App.Settings; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -using TagsCloud.Settings; namespace TagsCloud; @@ -23,9 +23,10 @@ public CloudForm(IUiAction[] actions, pictureBox.Dock = DockStyle.Fill; Controls.Add(pictureBox); } + protected override void OnShown(EventArgs e) { - base.OnShown(e); - Text = "TagCloud Painter"; + base.OnShown(e); + Text = "TagCloud Painter"; } } \ No newline at end of file diff --git a/TagsCloud/Infrastructure/AppSettings.cs b/TagsCloud/App/Infrastructure/AppSettings.cs similarity index 100% rename from TagsCloud/Infrastructure/AppSettings.cs rename to TagsCloud/App/Infrastructure/AppSettings.cs diff --git a/TagsCloud/App/Infrastructure/IImageHolder.cs b/TagsCloud/App/Infrastructure/IImageHolder.cs new file mode 100644 index 000000000..ecf59a3d0 --- /dev/null +++ b/TagsCloud/App/Infrastructure/IImageHolder.cs @@ -0,0 +1,13 @@ +using System.Drawing; +using TagsCloud.App.Settings; + +namespace TagsCloud.Infrastructure; + +public interface IImageHolder +{ + Size GetImageSize(); + Graphics StartDrawing(); + void UpdateUi(); + void RecreateImage(ImageSettings settings); + void SaveImage(string fileName); +} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/PictureBoxImageHolder.cs b/TagsCloud/App/Infrastructure/PictureBoxImageHolder.cs similarity index 94% rename from TagsCloud/Infrastructure/PictureBoxImageHolder.cs rename to TagsCloud/App/Infrastructure/PictureBoxImageHolder.cs index ba463bb8c..a0c78ce29 100644 --- a/TagsCloud/Infrastructure/PictureBoxImageHolder.cs +++ b/TagsCloud/App/Infrastructure/PictureBoxImageHolder.cs @@ -1,8 +1,7 @@ -using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; -using TagsCloud.Settings; +using TagsCloud.App.Settings; namespace TagsCloud.Infrastructure; @@ -20,12 +19,6 @@ public Graphics StartDrawing() return Graphics.FromImage(Image); } - private void FailIfNotInitialized() - { - if (Image == null) - throw new InvalidOperationException("Call PictureBoxImageHolder.RecreateImage before other method call!"); - } - public void UpdateUi() { Refresh(); @@ -42,4 +35,10 @@ public void SaveImage(string fileName) FailIfNotInitialized(); Image.Save(fileName); } + + private void FailIfNotInitialized() + { + if (Image == null) + throw new InvalidOperationException("Call PictureBoxImageHolder.RecreateImage before other method call!"); + } } \ No newline at end of file diff --git a/TagsCloud/App/Infrastructure/SettingsForm.cs b/TagsCloud/App/Infrastructure/SettingsForm.cs new file mode 100644 index 000000000..c46c78e6a --- /dev/null +++ b/TagsCloud/App/Infrastructure/SettingsForm.cs @@ -0,0 +1,37 @@ +using System.Windows.Forms; + +namespace TagsCloud.Infrastructure; + +public static class SettingsForm +{ + public static SettingsForm For(TSettings settings) + { + return new SettingsForm(settings); + } +} + +public class SettingsForm : Form +{ + public SettingsForm(TSettings settings) + { + var okButton = new Button + { + Text = "OK", + DialogResult = DialogResult.OK, + Dock = DockStyle.Bottom + }; + Controls.Add(okButton); + Controls.Add(new PropertyGrid + { + SelectedObject = settings, + Dock = DockStyle.Fill + }); + AcceptButton = okButton; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + Text = "Настройки"; + } +} \ No newline at end of file diff --git a/TagsCloud/App/Infrastructure/UiActions/EnumExtensions.cs b/TagsCloud/App/Infrastructure/UiActions/EnumExtensions.cs new file mode 100644 index 000000000..ed3ce1d2c --- /dev/null +++ b/TagsCloud/App/Infrastructure/UiActions/EnumExtensions.cs @@ -0,0 +1,17 @@ +using System.ComponentModel; + +namespace TagsCloud.Infrastructure.UiActions; + +public static class EnumExtensions +{ + public static string GetDescription(this Enum enumValue) + { + var fieldInfo = enumValue.GetType().GetField(enumValue.ToString()); + var description = fieldInfo + .GetCustomAttributes(typeof(DescriptionAttribute), false) + .Cast() + .FirstOrDefault()?.Description; + + return description ?? enumValue.ToString(); + } +} \ No newline at end of file diff --git a/TagsCloud/App/Infrastructure/UiActions/IUiAction.cs b/TagsCloud/App/Infrastructure/UiActions/IUiAction.cs new file mode 100644 index 000000000..5423d0d45 --- /dev/null +++ b/TagsCloud/App/Infrastructure/UiActions/IUiAction.cs @@ -0,0 +1,9 @@ +namespace TagsCloud.Infrastructure.UiActions; + +public interface IUiAction +{ + MenuCategory Category { get; } + string Name { get; } + string Description { get; } + void Perform(); +} \ No newline at end of file diff --git a/TagsCloud/App/Infrastructure/UiActions/MenuCategory.cs b/TagsCloud/App/Infrastructure/UiActions/MenuCategory.cs new file mode 100644 index 000000000..c67a8609f --- /dev/null +++ b/TagsCloud/App/Infrastructure/UiActions/MenuCategory.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace TagsCloud.Infrastructure.UiActions; + +public enum MenuCategory +{ + [Description("Файл")] File = 0, + + [Description("Типы Облаков")] Types = 1, + + [Description("Настойки")] Settings = 2 +} \ No newline at end of file diff --git a/TagsCloud/App/Infrastructure/UiActions/UiActionExtensions.cs b/TagsCloud/App/Infrastructure/UiActions/UiActionExtensions.cs new file mode 100644 index 000000000..fa436374e --- /dev/null +++ b/TagsCloud/App/Infrastructure/UiActions/UiActionExtensions.cs @@ -0,0 +1,32 @@ +using System.Windows.Forms; + +namespace TagsCloud.Infrastructure.UiActions; + +public static class UiActionExtensions +{ + public static ToolStripItem[] ToMenuItems(this IUiAction[] actions) + { + var items = actions.GroupBy(a => a.Category) + .OrderBy(a => a.Key) + .Select(g => CreateTopLevelMenuItem(g.Key, g.ToList())) + .Cast() + .ToArray(); + return items; + } + + private static ToolStripMenuItem CreateTopLevelMenuItem(MenuCategory category, IList items) + { + var menuItems = items.Select(a => a.ToMenuItem()).ToArray(); + return new ToolStripMenuItem(category.GetDescription(), null, menuItems); + } + + public static ToolStripItem ToMenuItem(this IUiAction action) + { + return + new ToolStripMenuItem(action.Name, null, (sender, args) => action.Perform()) + { + ToolTipText = action.Description, + Tag = action + }; + } +} \ No newline at end of file diff --git a/TagsCloud/Settings/ImageSettings.cs b/TagsCloud/App/Settings/ImageSettings.cs similarity index 77% rename from TagsCloud/Settings/ImageSettings.cs rename to TagsCloud/App/Settings/ImageSettings.cs index e921b9c8c..3ed378cb8 100644 --- a/TagsCloud/Settings/ImageSettings.cs +++ b/TagsCloud/App/Settings/ImageSettings.cs @@ -1,4 +1,4 @@ -namespace TagsCloud.Settings; +namespace TagsCloud.App.Settings; public class ImageSettings { diff --git a/TagsCloud/Settings/TagSettings.cs b/TagsCloud/App/Settings/TagSettings.cs similarity index 85% rename from TagsCloud/Settings/TagSettings.cs rename to TagsCloud/App/Settings/TagSettings.cs index 52133612b..2f22d3f84 100644 --- a/TagsCloud/Settings/TagSettings.cs +++ b/TagsCloud/App/Settings/TagSettings.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagsCloud.Settings; +namespace TagsCloud.App.Settings; public class TagSettings { diff --git a/TagsCloud/Settings/WordAnalyzerSettings.cs b/TagsCloud/App/Settings/WordAnalyzerSettings.cs similarity index 59% rename from TagsCloud/Settings/WordAnalyzerSettings.cs rename to TagsCloud/App/Settings/WordAnalyzerSettings.cs index fe2150aa6..1af0b4442 100644 --- a/TagsCloud/Settings/WordAnalyzerSettings.cs +++ b/TagsCloud/App/Settings/WordAnalyzerSettings.cs @@ -1,14 +1,14 @@ using TagsCloud.WordAnalyzer; -namespace TagsCloud; +namespace TagsCloud.App.Settings; public class WordAnalyzerSettings { - public List BoringWords { get; set; } = new(); + public List BoringWords { get; set; } = new(); public List ExcludedSpeeches { get; set; } = new() { - PartSpeech.Interjection, PartSpeech.Preposition, + PartSpeech.Interjection, PartSpeech.Preposition }; public List SelectedSpeeches { get; set; } = new(); diff --git a/TagsCloud/App/TagCloudPainter.cs b/TagsCloud/App/TagCloudPainter.cs new file mode 100644 index 000000000..09a1dc9cc --- /dev/null +++ b/TagsCloud/App/TagCloudPainter.cs @@ -0,0 +1,35 @@ +using TagsCloud.App.Settings; +using TagsCloud.CloudLayouter; +using TagsCloud.CloudVisualizer; +using TagsCloud.Infrastructure; + +namespace TagsCloud.App; + +public class TagCloudPainter +{ + private readonly FileReader fileReader; + + private readonly IImageHolder imageHolder; + private readonly TagSettings tagSettings; + private readonly WordAnalyzer.WordAnalyzer wordAnalyzer; + + public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAnalyzer.WordAnalyzer wordAnalyzer, + FileReader reader) + { + this.imageHolder = imageHolder; + this.wordAnalyzer = wordAnalyzer; + fileReader = reader; + this.tagSettings = tagSettings; + } + + public void Paint(string filePath, ISpiral spiral) + { + var parsedWords = wordAnalyzer.GetFrequencyList(fileReader.GetWords(filePath)); + using var graphic = imageHolder.StartDrawing(); + { + var painter = new TagCloudVisualizer(tagSettings, graphic, imageHolder.GetImageSize()); + painter.DrawTagCloud(spiral, parsedWords); + imageHolder.UpdateUi(); + } + } +} \ No newline at end of file diff --git a/TagsCloud/CloudLayouter/CloudLayouter.cs b/TagsCloud/CloudLayouter/CloudLayouter.cs index 88be5ec36..9b1adbeea 100644 --- a/TagsCloud/CloudLayouter/CloudLayouter.cs +++ b/TagsCloud/CloudLayouter/CloudLayouter.cs @@ -4,8 +4,6 @@ namespace TagsCloud.CloudLayouter; public class CloudLayouter : ICloudLayouter { - public List Rectangles { get; } - private readonly ISpiral spiral; public CloudLayouter(ISpiral spiral) @@ -14,6 +12,8 @@ public CloudLayouter(ISpiral spiral) Rectangles = new List(); } + public List Rectangles { get; } + public Rectangle PutNextRectangle(Size rectangleSize) { if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) diff --git a/TagsCloud/CloudLayouter/FlowerSpiral.cs b/TagsCloud/CloudLayouter/FlowerSpiral.cs index 8abcf4bcb..29cf54284 100644 --- a/TagsCloud/CloudLayouter/FlowerSpiral.cs +++ b/TagsCloud/CloudLayouter/FlowerSpiral.cs @@ -4,15 +4,15 @@ namespace TagsCloud.CloudLayouter; public class FlowerSpiral : ISpiral { - private int counter; - private readonly float step; - private readonly Point center; private readonly int petalCount; private readonly double petalLength; + private readonly float step; + private Point center; + private int counter; - public FlowerSpiral(Point center, double petalLength = 0.25, int petalCount = 1, float step = 0.1f) + public FlowerSpiral(Point center, double petalLength = 0.5, int petalCount = 4, float step = 0.1f) { - this.center = center; + Init(center); this.petalLength = petalLength; this.petalCount = petalCount; if (this.petalCount < 0 || petalLength < 0) @@ -22,6 +22,12 @@ public FlowerSpiral(Point center, double petalLength = 0.25, int petalCount = 1, this.step = step; } + public void Init(Point center) + { + counter = 0; + this.center = center; + } + public Point GetPoint() { var angle = step * counter; diff --git a/TagsCloud/CloudLayouter/ISpiral.cs b/TagsCloud/CloudLayouter/ISpiral.cs index f425a8dc3..bdcd05e03 100644 --- a/TagsCloud/CloudLayouter/ISpiral.cs +++ b/TagsCloud/CloudLayouter/ISpiral.cs @@ -5,4 +5,5 @@ namespace TagsCloud.CloudLayouter; public interface ISpiral { Point GetPoint(); + void Init(Point center); } \ No newline at end of file diff --git a/TagsCloud/CloudLayouter/Spiral.cs b/TagsCloud/CloudLayouter/Spiral.cs index 02351dff2..39aded40b 100644 --- a/TagsCloud/CloudLayouter/Spiral.cs +++ b/TagsCloud/CloudLayouter/Spiral.cs @@ -4,18 +4,24 @@ namespace TagsCloud.CloudLayouter; public class Spiral : ISpiral { - private int counter; private readonly float step; - private readonly Point center; + private Point center; + private int counter; public Spiral(Point center, float step = 0.1f) { - this.center = center; + Init(center); if (step == 0) throw new ArgumentException("the step must not be equal to 0"); this.step = step; } + public void Init(Point center) + { + counter = 0; + this.center = center; + } + public Point GetPoint() { var angle = step * counter; diff --git a/TagsCloud/CloudPainter/Tag.cs b/TagsCloud/CloudVisualizer/Tag.cs similarity index 100% rename from TagsCloud/CloudPainter/Tag.cs rename to TagsCloud/CloudVisualizer/Tag.cs index 19d5e5b1f..8c6448dc4 100644 --- a/TagsCloud/CloudPainter/Tag.cs +++ b/TagsCloud/CloudVisualizer/Tag.cs @@ -6,8 +6,8 @@ public class Tag { public readonly Font Font; public readonly string Word; - public Rectangle Rectangle; public Color Color; + public Rectangle Rectangle; public Tag(Font font, string word, Rectangle rectangle, Color color) { diff --git a/TagsCloud/CloudPainter/TagCloudPainter.cs b/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs similarity index 53% rename from TagsCloud/CloudPainter/TagCloudPainter.cs rename to TagsCloud/CloudVisualizer/TagCloudVisualizer.cs index d0b5692d9..1a642ba2c 100644 --- a/TagsCloud/CloudPainter/TagCloudPainter.cs +++ b/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs @@ -1,40 +1,34 @@ using System.Drawing; using System.Windows.Forms; +using TagsCloud.App.Settings; using TagsCloud.CloudLayouter; -using TagsCloud.Infrastructure; -using TagsCloud.Settings; +using TagsCloud.CloudPainter; using TagsCloud.WordAnalyzer; -namespace TagsCloud.CloudPainter; +namespace TagsCloud.CloudVisualizer; -public class TagCloudPainter +public class TagCloudVisualizer { - - private readonly IImageHolder imageHolder; + private readonly Graphics graphics; + private readonly Size sizeImage; private readonly TagSettings tagSettings; - private readonly WordAnalyzer.WordAnalyzer wordAnalyzer; - private readonly FileReader fileReader; - public TagCloudPainter(IImageHolder imageHolder, TagSettings tagSettings, WordAnalyzer.WordAnalyzer wordAnalyzer, - FileReader reader) + public TagCloudVisualizer(TagSettings tagSettings, Graphics graphics, Size imageSize) { - this.imageHolder = imageHolder; + this.graphics = graphics; + sizeImage = imageSize; this.tagSettings = tagSettings; - this.wordAnalyzer = wordAnalyzer; - fileReader = reader; } - public void Paint(ISpiral spiral, string path) + + public void DrawTagCloud(ISpiral spiral, IEnumerable wordInfo) { - using var graphics = imageHolder.StartDrawing(); - var sizeImage = imageHolder.GetImageSize(); + spiral.Init(new Point(sizeImage.Width / 2, sizeImage.Height / 2)); var cloudLayouter = new CloudLayouter.CloudLayouter(spiral); - var wordList = fileReader.GetWords(path); var background = new SolidBrush(Color.Black); graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); - DrawTagsCloud(wordAnalyzer.GetFrequencyList(wordList), graphics, cloudLayouter); - imageHolder.UpdateUi(); + DrawTagsCloud(wordInfo, graphics, cloudLayouter); } - + private Tag GetTag(WordInfo wordInfo, ICloudLayouter cloudLayouter) { var font = new Font(tagSettings.FontFamily, wordInfo.Count * tagSettings.Size); @@ -43,7 +37,7 @@ private Tag GetTag(WordInfo wordInfo, ICloudLayouter cloudLayouter) return new Tag(font, wordInfo.Word, textRectangle, tagSettings.Color); } - private void DrawTagsCloud(IEnumerablewords,Graphics graphics, ICloudLayouter cloudLayouter) + private void DrawTagsCloud(IEnumerable words, Graphics graphics, ICloudLayouter cloudLayouter) { foreach (var word in words) { diff --git a/TagsCloud/FileReader/DocxParser.cs b/TagsCloud/FileReader/DocxParser.cs index c30d49350..a75c59e55 100644 --- a/TagsCloud/FileReader/DocxParser.cs +++ b/TagsCloud/FileReader/DocxParser.cs @@ -5,6 +5,7 @@ namespace TagsCloud; public class DocxParser : IParser { public string FileType => "docx"; + public IEnumerable GetWordList(string filePath) { var document = new Document(filePath, FileFormat.Docx); diff --git a/TagsCloud/FileReader/IParser.cs b/TagsCloud/FileReader/IParser.cs index 705e3692a..db7a8bbed 100644 --- a/TagsCloud/FileReader/IParser.cs +++ b/TagsCloud/FileReader/IParser.cs @@ -1,5 +1,3 @@ -using Spire.Doc; - namespace TagsCloud; public interface IParser diff --git a/TagsCloud/FileReader/ParserHelper.cs b/TagsCloud/FileReader/ParserHelper.cs index 62c62f83a..011cf75a9 100644 --- a/TagsCloud/FileReader/ParserHelper.cs +++ b/TagsCloud/FileReader/ParserHelper.cs @@ -1,5 +1,4 @@ using System.Text.RegularExpressions; -using Spire.Doc; using Spire.Doc.Interface; namespace TagsCloud; @@ -7,15 +6,12 @@ namespace TagsCloud; public static class ParserHelper { private static Regex SelectAllWordsRegex => new(@"([\w]+)", RegexOptions.Compiled); + public static IEnumerable GetTextParagraph(IDocument document) { var section = document.Sections[0]; for (var i = 0; i < section.Paragraphs.Count; i++) - { foreach (var word in SelectAllWordsRegex.Matches(section.Paragraphs[i].Text)) - { yield return word.ToString().Trim().ToLower(); - } - } } } \ No newline at end of file diff --git a/TagsCloud/FileReader/TxtParser.cs b/TagsCloud/FileReader/TxtParser.cs index 5371f0c58..6f5b9827e 100644 --- a/TagsCloud/FileReader/TxtParser.cs +++ b/TagsCloud/FileReader/TxtParser.cs @@ -5,6 +5,7 @@ namespace TagsCloud; public class TxtParser : IParser { public string FileType => "txt"; + public IEnumerable GetWordList(string filePath) { var document = new Document(filePath, FileFormat.Txt); diff --git a/TagsCloud/Folder.DotSettings b/TagsCloud/Folder.DotSettings index 06b71d5de..2c6ff163c 100644 --- a/TagsCloud/Folder.DotSettings +++ b/TagsCloud/Folder.DotSettings @@ -1,2 +1,4 @@ - + False \ No newline at end of file diff --git a/TagsCloud/Infrastructure/IImageHolder.cs b/TagsCloud/Infrastructure/IImageHolder.cs deleted file mode 100644 index 1ee763031..000000000 --- a/TagsCloud/Infrastructure/IImageHolder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Drawing; -using TagsCloud.Settings; - -namespace TagsCloud.Infrastructure -{ - public interface IImageHolder - { - Size GetImageSize(); - Graphics StartDrawing(); - void UpdateUi(); - void RecreateImage(ImageSettings settings); - void SaveImage(string fileName); - } -} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/SettingsForm.cs b/TagsCloud/Infrastructure/SettingsForm.cs deleted file mode 100644 index da9bd1593..000000000 --- a/TagsCloud/Infrastructure/SettingsForm.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Windows.Forms; - -namespace TagsCloud.Infrastructure -{ - public static class SettingsForm - { - public static SettingsForm For(TSettings settings) - { - return new SettingsForm(settings); - } - } - - public class SettingsForm : Form - { - public SettingsForm(TSettings settings) - { - var okButton = new Button - { - Text = "OK", - DialogResult = DialogResult.OK, - Dock = DockStyle.Bottom, - }; - Controls.Add(okButton); - Controls.Add(new PropertyGrid - { - SelectedObject = settings, - Dock = DockStyle.Fill - }); - AcceptButton = okButton; - } - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - Text = "Настройки"; - } - } -} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/EnumExtensions.cs b/TagsCloud/Infrastructure/UiActions/EnumExtensions.cs deleted file mode 100644 index 23994225e..000000000 --- a/TagsCloud/Infrastructure/UiActions/EnumExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel; - -namespace TagsCloud.Infrastructure.UiActions -{ - public static class EnumExtensions - { - public static string GetDescription(this Enum enumValue) - { - var fieldInfo = enumValue.GetType().GetField(enumValue.ToString()); - var description = fieldInfo - .GetCustomAttributes(typeof(DescriptionAttribute), false) - .Cast() - .FirstOrDefault()?.Description; - - return description ?? enumValue.ToString(); - } - } -} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/IUiAction.cs b/TagsCloud/Infrastructure/UiActions/IUiAction.cs deleted file mode 100644 index f0ae383f7..000000000 --- a/TagsCloud/Infrastructure/UiActions/IUiAction.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace TagsCloud.Infrastructure.UiActions -{ - public interface IUiAction - { - MenuCategory Category { get; } - string Name { get; } - string Description { get; } - void Perform(); - } -} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/MenuCategory.cs b/TagsCloud/Infrastructure/UiActions/MenuCategory.cs deleted file mode 100644 index 6a4a2603a..000000000 --- a/TagsCloud/Infrastructure/UiActions/MenuCategory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel; - -namespace TagsCloud.Infrastructure.UiActions -{ - public enum MenuCategory - { - [Description("Файл")] - File = 0, - - [Description("Типы Облаков")] - Types = 1, - - [Description("Настойки")] - Settings = 2, - } -} \ No newline at end of file diff --git a/TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs b/TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs deleted file mode 100644 index 57e9cf900..000000000 --- a/TagsCloud/Infrastructure/UiActions/UiActionExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Windows.Forms; - -namespace TagsCloud.Infrastructure.UiActions -{ - public static class UiActionExtensions - { - public static ToolStripItem[] ToMenuItems(this IUiAction[] actions) - { - var items = actions.GroupBy(a => a.Category) - .OrderBy(a => a.Key) - .Select(g => CreateTopLevelMenuItem(g.Key, g.ToList())) - .Cast() - .ToArray(); - return items; - } - - private static ToolStripMenuItem CreateTopLevelMenuItem(MenuCategory category, IList items) - { - var menuItems = items.Select(a => a.ToMenuItem()).ToArray(); - return new ToolStripMenuItem(category.GetDescription(), null, menuItems); - } - - public static ToolStripItem ToMenuItem(this IUiAction action) - { - return - new ToolStripMenuItem(action.Name, null, (sender, args) => action.Perform()) - { - ToolTipText = action.Description, - Tag = action - }; - } - } -} \ No newline at end of file diff --git a/TagsCloud/ProgramConstructor.cs b/TagsCloud/ProgramConstructor.cs index 8af15d7d8..a2bb5f9c8 100644 --- a/TagsCloud/ProgramConstructor.cs +++ b/TagsCloud/ProgramConstructor.cs @@ -1,9 +1,11 @@ +using System.Drawing; using System.Reflection; using Autofac; -using TagsCloud.CloudPainter; +using TagsCloud.App; +using TagsCloud.App.Settings; +using TagsCloud.CloudLayouter; using TagsCloud.Infrastructure; using TagsCloud.Infrastructure.UiActions; -using TagsCloud.Settings; namespace TagsCloud; @@ -20,14 +22,25 @@ public static IContainer ConstructProgram() .AsImplementedInterfaces(); containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType(); - containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType().As() .SingleInstance(); containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType().AsSelf().SingleInstance(); containerBuilder.RegisterType(); + containerBuilder.RegisterType(); containerBuilder.RegisterType().AsSelf().SingleInstance(); + containerBuilder.Register(c => + { + var appSettings = c.Resolve(); + return new FlowerSpiral(new Point(appSettings.Width / 2, appSettings.Height / 2)); + }).AsSelf(); + + containerBuilder.Register(c => + { + var imageSettings = c.Resolve(); + return new Spiral(new Point(imageSettings.Width / 2, imageSettings.Height / 2)); + }).AsSelf(); return containerBuilder.Build(); } } \ No newline at end of file diff --git a/TagsCloud/TagsCloud.csproj b/TagsCloud/TagsCloud.csproj index ba08daa00..7d53b2496 100644 --- a/TagsCloud/TagsCloud.csproj +++ b/TagsCloud/TagsCloud.csproj @@ -9,29 +9,26 @@ - - - - + + + + - - ..\..\..\..\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\3.1.32\System.Windows.Forms.dll - + + ..\..\..\..\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\3.1.32\System.Windows.Forms.dll + - - Form - - - Component - + - + + Form + diff --git a/TagsCloud/TagsCloud.csproj.DotSettings b/TagsCloud/TagsCloud.csproj.DotSettings index ca5994cdb..889c657ff 100644 --- a/TagsCloud/TagsCloud.csproj.DotSettings +++ b/TagsCloud/TagsCloud.csproj.DotSettings @@ -1,3 +1,6 @@ - + True - True \ No newline at end of file + True \ No newline at end of file diff --git a/TagsCloud/WordAnalyzer/WordAnalyzer.cs b/TagsCloud/WordAnalyzer/WordAnalyzer.cs index 1e2f30cf7..7fa58cdbe 100644 --- a/TagsCloud/WordAnalyzer/WordAnalyzer.cs +++ b/TagsCloud/WordAnalyzer/WordAnalyzer.cs @@ -1,4 +1,5 @@ using DeepMorphy; +using TagsCloud.App.Settings; namespace TagsCloud.WordAnalyzer; @@ -17,10 +18,12 @@ private IEnumerable GetFilteredWords(IEnumerable words) var selectedSpeeches = WordAnalyzerHelper.GetConvertedSpeeches(Settings.SelectedSpeeches); var morphAnalyzer = new MorphAnalyzer(true); var morphInfos = morphAnalyzer.Parse(words); - return morphInfos.Where(morphInfo => !Settings.BoringWords.Any(item => item.Equals(morphInfo.BestTag.Lemma, StringComparison.OrdinalIgnoreCase)) && - !excludedSpeeches.Contains(morphInfo.BestTag["чр"]) && - (selectedSpeeches.Count == 0 || - selectedSpeeches.Contains(morphInfo.BestTag["чр"]))) + return morphInfos.Where(morphInfo => + !Settings.BoringWords.Any(item => + item.Equals(morphInfo.BestTag.Lemma, StringComparison.OrdinalIgnoreCase)) && + !excludedSpeeches.Contains(morphInfo.BestTag["чр"]) && + (selectedSpeeches.Count == 0 || + selectedSpeeches.Contains(morphInfo.BestTag["чр"]))) .Select(info => info.BestTag.HasLemma ? info.BestTag.Lemma : info.Text); } diff --git a/TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs b/TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs index 37811874f..92a5fadd4 100644 --- a/TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs +++ b/TagsCloud/WordAnalyzer/WordAnalyzerHelper.cs @@ -15,7 +15,7 @@ public static class WordAnalyzerHelper { PartSpeech.Preposition, "предл" }, { PartSpeech.Interjection, "межд" } }; - + public static List GetConvertedSpeeches(IEnumerable speeches) { return speeches.Select(speech => converter[speech]).ToList(); diff --git a/TagsCloudTests/AnalyzerTests.cs b/TagsCloudTests/AnalyzerTests.cs index 830cc4d72..ed1111eda 100644 --- a/TagsCloudTests/AnalyzerTests.cs +++ b/TagsCloudTests/AnalyzerTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using TagsCloud; +using TagsCloud.App.Settings; using TagsCloud.WordAnalyzer; namespace TagsCloudTests; From 4bfb19ecbb57a219e4a14cadbf588edd92385ab8 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Tue, 30 Jan 2024 04:28:49 +0500 Subject: [PATCH 09/10] update after feedback --- TagsCloud/CloudLayouter/CloudLayouter.cs | 14 ++++----- TagsCloud/CloudLayouter/FlowerSpiral.cs | 28 +++++++----------- TagsCloud/CloudLayouter/ISpiral.cs | 4 +-- TagsCloud/CloudLayouter/Spiral.cs | 29 ++++++++----------- .../CloudVisualizer/TagCloudVisualizer.cs | 8 +++-- TagsCloud/ProgramConstructor.cs | 13 ++------- TagsCloudTests/FlowerSpiralTests.cs | 29 ++++++++----------- TagsCloudTests/LayouterTests.cs | 4 +-- TagsCloudTests/SpiralTests.cs | 11 ++----- 9 files changed, 57 insertions(+), 83 deletions(-) diff --git a/TagsCloud/CloudLayouter/CloudLayouter.cs b/TagsCloud/CloudLayouter/CloudLayouter.cs index 9b1adbeea..627762c0f 100644 --- a/TagsCloud/CloudLayouter/CloudLayouter.cs +++ b/TagsCloud/CloudLayouter/CloudLayouter.cs @@ -4,11 +4,11 @@ namespace TagsCloud.CloudLayouter; public class CloudLayouter : ICloudLayouter { - private readonly ISpiral spiral; + private readonly IEnumerator iterator; - public CloudLayouter(ISpiral spiral) + public CloudLayouter(ISpiral spiral, Point center) { - this.spiral = spiral; + iterator = spiral.GetPoints(center).GetEnumerator(); Rectangles = new List(); } @@ -25,12 +25,12 @@ public Rectangle PutNextRectangle(Size rectangleSize) private Rectangle CreateNextRectangle(Size rectangleSize) { - var point = spiral.GetPoint(); - var rectangle = new Rectangle(point, rectangleSize); + iterator.MoveNext(); + var rectangle = new Rectangle(iterator.Current, rectangleSize); while (!HasNoIntersections(rectangle)) { - point = spiral.GetPoint(); - rectangle = new Rectangle(point, rectangleSize); + iterator.MoveNext(); + rectangle = new Rectangle(iterator.Current, rectangleSize); } return rectangle; diff --git a/TagsCloud/CloudLayouter/FlowerSpiral.cs b/TagsCloud/CloudLayouter/FlowerSpiral.cs index 29cf54284..f07492274 100644 --- a/TagsCloud/CloudLayouter/FlowerSpiral.cs +++ b/TagsCloud/CloudLayouter/FlowerSpiral.cs @@ -7,12 +7,9 @@ public class FlowerSpiral : ISpiral private readonly int petalCount; private readonly double petalLength; private readonly float step; - private Point center; private int counter; - - public FlowerSpiral(Point center, double petalLength = 0.5, int petalCount = 4, float step = 0.1f) + public FlowerSpiral(double petalLength = 0.5, int petalCount = 4, float step = 0.1f) { - Init(center); this.petalLength = petalLength; this.petalCount = petalCount; if (this.petalCount < 0 || petalLength < 0) @@ -22,20 +19,17 @@ public FlowerSpiral(Point center, double petalLength = 0.5, int petalCount = 4, this.step = step; } - public void Init(Point center) + public IEnumerable GetPoints(Point center) { counter = 0; - this.center = center; - } - - public Point GetPoint() - { - var angle = step * counter; - var radius = angle * petalLength * Math.Sin(petalCount * angle); - var xOffset = (float)(radius * Math.Cos(angle)); - var yOffset = (float)(radius * Math.Sin(angle)); - var point = new Point((int)Math.Round(center.X + xOffset), (int)Math.Round(center.Y + yOffset)); - counter += 1; - return point; + while (true) + { + var angle = step * counter; + var radius = angle * petalLength * Math.Sin(petalCount * angle); + var xOffset = (float)(radius * Math.Cos(angle)); + var yOffset = (float)(radius * Math.Sin(angle)); + yield return new Point((int)Math.Round(center.X + xOffset), (int)Math.Round(center.Y + yOffset)); + counter += 1; + } } } \ No newline at end of file diff --git a/TagsCloud/CloudLayouter/ISpiral.cs b/TagsCloud/CloudLayouter/ISpiral.cs index bdcd05e03..f9786e786 100644 --- a/TagsCloud/CloudLayouter/ISpiral.cs +++ b/TagsCloud/CloudLayouter/ISpiral.cs @@ -1,9 +1,9 @@ +using System.Collections; using System.Drawing; namespace TagsCloud.CloudLayouter; public interface ISpiral { - Point GetPoint(); - void Init(Point center); + IEnumerable GetPoints(Point start); } \ No newline at end of file diff --git a/TagsCloud/CloudLayouter/Spiral.cs b/TagsCloud/CloudLayouter/Spiral.cs index 39aded40b..4029627fa 100644 --- a/TagsCloud/CloudLayouter/Spiral.cs +++ b/TagsCloud/CloudLayouter/Spiral.cs @@ -5,30 +5,25 @@ namespace TagsCloud.CloudLayouter; public class Spiral : ISpiral { private readonly float step; - private Point center; - private int counter; - public Spiral(Point center, float step = 0.1f) + public Spiral(float step = 0.1f) { - Init(center); if (step == 0) throw new ArgumentException("the step must not be equal to 0"); this.step = step; } - public void Init(Point center) + public IEnumerable GetPoints(Point start) { - counter = 0; - this.center = center; - } - - public Point GetPoint() - { - var angle = step * counter; - var xOffset = (float)(step * angle * Math.Cos(angle)); - var yOffset = (float)(step * angle * Math.Sin(angle)); - var point = new Point((int)Math.Round(center.X + xOffset), (int)Math.Round(center.Y + yOffset)); - counter += 1; - return point; + var counter = 0; + while (true) + { + var angle = step * counter; + var xOffset = (float)(step * angle * Math.Cos(angle)); + var yOffset = (float)(step * angle * Math.Sin(angle)); + var point = new Point((int)Math.Round(start.X + xOffset), (int)Math.Round(start.Y + yOffset)); + yield return point; + counter += 1; + } } } \ No newline at end of file diff --git a/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs b/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs index 1a642ba2c..06ba70584 100644 --- a/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs +++ b/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Drawing; using System.Windows.Forms; using TagsCloud.App.Settings; @@ -22,8 +23,7 @@ public TagCloudVisualizer(TagSettings tagSettings, Graphics graphics, Size image public void DrawTagCloud(ISpiral spiral, IEnumerable wordInfo) { - spiral.Init(new Point(sizeImage.Width / 2, sizeImage.Height / 2)); - var cloudLayouter = new CloudLayouter.CloudLayouter(spiral); + var cloudLayouter = new CloudLayouter.CloudLayouter(spiral, new Point(sizeImage.Width / 2, sizeImage.Height / 2)); var background = new SolidBrush(Color.Black); graphics.FillRectangle(background, new Rectangle(0, 0, sizeImage.Width, sizeImage.Height)); DrawTagsCloud(wordInfo, graphics, cloudLayouter); @@ -31,9 +31,13 @@ public void DrawTagCloud(ISpiral spiral, IEnumerable wordInfo) private Tag GetTag(WordInfo wordInfo, ICloudLayouter cloudLayouter) { + var f = new Stopwatch(); var font = new Font(tagSettings.FontFamily, wordInfo.Count * tagSettings.Size); var textSize = TextRenderer.MeasureText(wordInfo.Word, font); + f.Start(); var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); + f.Stop(); + Console.WriteLine(f.Elapsed); return new Tag(font, wordInfo.Word, textRectangle, tagSettings.Color); } diff --git a/TagsCloud/ProgramConstructor.cs b/TagsCloud/ProgramConstructor.cs index a2bb5f9c8..eeb878bab 100644 --- a/TagsCloud/ProgramConstructor.cs +++ b/TagsCloud/ProgramConstructor.cs @@ -30,17 +30,8 @@ public static IContainer ConstructProgram() containerBuilder.RegisterType(); containerBuilder.RegisterType(); containerBuilder.RegisterType().AsSelf().SingleInstance(); - containerBuilder.Register(c => - { - var appSettings = c.Resolve(); - return new FlowerSpiral(new Point(appSettings.Width / 2, appSettings.Height / 2)); - }).AsSelf(); - - containerBuilder.Register(c => - { - var imageSettings = c.Resolve(); - return new Spiral(new Point(imageSettings.Width / 2, imageSettings.Height / 2)); - }).AsSelf(); + containerBuilder.RegisterType(); + containerBuilder.RegisterType(); return containerBuilder.Build(); } } \ No newline at end of file diff --git a/TagsCloudTests/FlowerSpiralTests.cs b/TagsCloudTests/FlowerSpiralTests.cs index 500e2d23c..902e34046 100644 --- a/TagsCloudTests/FlowerSpiralTests.cs +++ b/TagsCloudTests/FlowerSpiralTests.cs @@ -1,6 +1,5 @@ using System.Drawing; using FluentAssertions; -using TagsCloud; using TagsCloud.CloudLayouter; namespace TagsCloudTests; @@ -8,7 +7,7 @@ namespace TagsCloudTests; public class FlowerSpiralTests { private Point center; - + [SetUp] public void Setup() { @@ -18,37 +17,33 @@ public void Setup() private static IEnumerable ConstructorSpiralPoints => new[] { new TestCaseData(0, 6, 2.5f, - new Point[] { new(10, 10), new(10, 10),new(10, 10) }) + new Point[] { new(10, 10), new(10, 10), new(10, 10) }) .SetName("petal Length equals 0"), new TestCaseData(6, 0, 2.5f, - new Point[] {new(10, 10), new(10, 10),new(10, 10) }) + new Point[] { new(10, 10), new(10, 10), new(10, 10) }) .SetName("petal Count equals 0"), new TestCaseData(4, 4, 2.5f, - new Point[] {new(10, 10), new(14, 7),new(15, -8), new(0, -18), new(-15, -6), new(-3, 11) }) + new Point[] { new(10, 10), new(14, 7), new(15, -8), new(0, -18), new(-15, -6), new(-3, 11) }) .SetName("AngleStep is positive"), new TestCaseData(4, 4, -2.5f, - new Point[] {new(10, 10), new(14, 13),new(15, 28), new(0, 38), new(-15, 26), new(-3, 9) }) + new Point[] { new(10, 10), new(14, 13), new(15, 28), new(0, 38), new(-15, 26), new(-3, 9) }) .SetName("AngleStep is negative") }; - + [Test] public void FlowerSpiral_WhenPetalCountAndPetalLengthLessThat0_ShouldBeThrowException() { - Action action = () => new FlowerSpiral(center, -1, -1); + Action action = () => new FlowerSpiral(-1, -1); action.Should().Throw() - .WithMessage($"petalCount or petalLength must not be less than 0"); + .WithMessage("petalCount or petalLength must not be less than 0"); } [TestCaseSource(nameof(ConstructorSpiralPoints))] - public void FlowerSpiral_GetNextPoint_CreatePointsWithCustomAngle_ReturnsCorrectPoints(double petalLength, int petalCount, float angleStep, + public void FlowerSpiral_GetNextPoint_CreatePointsWithCustomAngle_ReturnsCorrectPoints(double petalLength, + int petalCount, float angleStep, Point[] expectedPoints) { - var spiral = new FlowerSpiral(center,petalLength, petalCount, angleStep); - var resultPoints = new Point[expectedPoints.Length]; - for (var i = 0; i < resultPoints.Length; i++) - resultPoints[i] = spiral.GetPoint(); - - resultPoints.Should().BeEquivalentTo(expectedPoints); + var spiral = new FlowerSpiral(petalLength, petalCount, angleStep); + spiral.GetPoints(center).Take(expectedPoints.Length).Should().BeEquivalentTo(expectedPoints); } - } \ No newline at end of file diff --git a/TagsCloudTests/LayouterTests.cs b/TagsCloudTests/LayouterTests.cs index acf6ec5db..e3452bbae 100644 --- a/TagsCloudTests/LayouterTests.cs +++ b/TagsCloudTests/LayouterTests.cs @@ -15,8 +15,8 @@ public class LayouterTests [SetUp] public void SetUp() { - spiral = new Spiral(new Point(10, 10)); - sut = new CloudLayouter(spiral); + spiral = new Spiral(); + sut = new CloudLayouter(spiral, new Point(10, 10)); } diff --git a/TagsCloudTests/SpiralTests.cs b/TagsCloudTests/SpiralTests.cs index ce7561afc..c84272616 100644 --- a/TagsCloudTests/SpiralTests.cs +++ b/TagsCloudTests/SpiralTests.cs @@ -1,6 +1,5 @@ using System.Drawing; using FluentAssertions; -using TagsCloud; using TagsCloud.CloudLayouter; namespace TagsCloudTests; @@ -28,7 +27,7 @@ public void Setup() [Test] public void Spiral_StepAngleEquals0_ShouldBeThrowException() { - Action action = () => new Spiral(center, 0); + Action action = () => new Spiral(0); action.Should().Throw() .WithMessage("the step must not be equal to 0"); } @@ -37,11 +36,7 @@ public void Spiral_StepAngleEquals0_ShouldBeThrowException() public void Spiral_GetNextPoint_CreatePointsWithCustomAngle_ReturnsCorrectPoints(float angleStep, Point[] expectedPoints) { - var spiral = new Spiral(new Point(10, 10), angleStep); - var resultPoints = new Point[expectedPoints.Length]; - for (var i = 0; i < resultPoints.Length; i++) - resultPoints[i] = spiral.GetPoint(); - - resultPoints.Should().BeEquivalentTo(expectedPoints); + var spiral = new Spiral(angleStep); + spiral.GetPoints(center).Take(expectedPoints.Length).Should().BeEquivalentTo(expectedPoints); } } \ No newline at end of file From eff85b91377c2c7c532bdbfb0b1fae0f1288fc30 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Tue, 30 Jan 2024 07:17:54 +0500 Subject: [PATCH 10/10] remove stopwatch --- TagsCloud/CloudVisualizer/TagCloudVisualizer.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs b/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs index 06ba70584..b8fc63e93 100644 --- a/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs +++ b/TagsCloud/CloudVisualizer/TagCloudVisualizer.cs @@ -31,13 +31,9 @@ public void DrawTagCloud(ISpiral spiral, IEnumerable wordInfo) private Tag GetTag(WordInfo wordInfo, ICloudLayouter cloudLayouter) { - var f = new Stopwatch(); var font = new Font(tagSettings.FontFamily, wordInfo.Count * tagSettings.Size); var textSize = TextRenderer.MeasureText(wordInfo.Word, font); - f.Start(); var textRectangle = cloudLayouter.PutNextRectangle(new Size(textSize.Width, textSize.Height)); - f.Stop(); - Console.WriteLine(f.Elapsed); return new Tag(font, wordInfo.Word, textRectangle, tagSettings.Color); }