diff --git a/Client/Client.csproj b/Client/Client.csproj new file mode 100644 index 00000000..57477690 --- /dev/null +++ b/Client/Client.csproj @@ -0,0 +1,26 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + Always + + + Always + + + Always + + + + diff --git a/Client/DependencyInjection.cs b/Client/DependencyInjection.cs new file mode 100644 index 00000000..4be2c332 --- /dev/null +++ b/Client/DependencyInjection.cs @@ -0,0 +1,69 @@ +using Autofac; +using TagsCloudContainer; +using TagsCloudContainer.ColorProviders; +using TagsCloudContainer.Configuration; +using TagsCloudContainer.PointGenerators; +using TagsCloudContainer.StringParsers; +using TagsCloudContainer.TagGenerator; +using TagsCloudContainer.TextProcessor; +using TagsCloudContainer.TextProviders; +using TagsCloudContainer.WordFilters; + +namespace Client; + +public class DependencyInjection +{ + public static IContainer BuildContainer(Config config) + { + var container = new ContainerBuilder(); + container.RegisterInstance(config).AsSelf(); + + container.RegisterType(config.SupportedReadingFormats[Path.GetExtension(config.FilePath)]) + .As() + .WithParameter("filePath", config.FilePath) + .SingleInstance(); + container.RegisterType() + .As() + .SingleInstance(); + + container.RegisterType(config.PointGenerator) + .As() + .SingleInstance(); + + if (config.Color != null) + container.RegisterType() + .As() + .WithParameter("color", config.Color) + .SingleInstance(); + else + container.RegisterType() + .As() + .SingleInstance(); + + container.RegisterType() + .As() + .SingleInstance(); + + container.RegisterType() + .As() + .WithParameter("defaultFont", config.Font) + .SingleInstance(); + + + + container.RegisterType().As() + .SingleInstance(); + container.RegisterType().As() + .SingleInstance(); + container.RegisterType().As() + .SingleInstance(); + + container.RegisterType() + .AsSelf() + .WithParameter("fileName", config.PicturePath) + .WithParameter("startPoint", config.StartPoint) + .SingleInstance(); + + return container.Build(); + } +} \ No newline at end of file diff --git a/Client/Program.cs b/Client/Program.cs new file mode 100644 index 00000000..f9db896b --- /dev/null +++ b/Client/Program.cs @@ -0,0 +1,133 @@ +using System.Drawing; +using System.Reflection; +using TagsCloudContainer.Configuration; +using TagsCloudContainer.PointGenerators; +using TagsCloudContainer.TextProviders; +using TagsCloudContainer; +using Autofac; + +namespace Client +{ + internal class Program + { + static void Main() + { + var config = new Config(); + + ConfigureSupportedReadingFormats(config); + ConfigureFileSource(config); + ConfigureCloudView(config); + ConfigureColor(config); + ConfigurePathToSave(config); + ConfigureStartPoint(config); + ConfigureFont(config); + + var container = DependencyInjection.BuildContainer(config); + using var scope = container.BeginLifetimeScope(); + scope.Resolve().DrawPicture(); + Console.WriteLine($"результат сохранен в {config.PicturePath}"); + } + + private static void ConfigureSupportedReadingFormats(Config config) + { + Console.WriteLine("Поддерживаются следующие форматы файлов для чтения:"); + var textProviders = FindImplemetations(); + foreach (var point in textProviders) + Console.WriteLine("\t" + point.Key); + config.SupportedReadingFormats = textProviders; + } + + private static void ConfigureFont(Config config) + { + config.Font = new Font("arial", 12); + } + + private static void ConfigurePathToSave(Config config) + { + Console.WriteLine("Введите полный путь и название файла для сохранения"); + var inp = Console.ReadLine(); + config.PicturePath = inp.Length == 0 ? "1.bmp" : inp; + } + + private static void ConfigureStartPoint(Config config) + { + Console.WriteLine("Введите координаты центра поля для рисования" + + "\n При некорректном вводе координаты центра составят ( 1000, 1000)"); + var xLine = ReadValue("Координата Х"); + var yLine = ReadValue("Координата Y"); + if (int.TryParse(xLine, out var xResult) && + int.TryParse(yLine, out var yResult)) + config.StartPoint = new Point(xResult, yResult); + config.StartPoint = new Point(1000, 1000); + } + + private static void ConfigureFileSource(Config config) + { + Console.WriteLine("Введите имя файла источника тэгов"); + var inp = Console.ReadLine(); + config.FilePath = inp.Length == 0 ? @"TestFile.txt" : inp; + } + + private static string GetLabel(RainbowColors color) + { + var fieldInfo = color.GetType().GetField(color.ToString()); + var attribute = (LabelAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(LabelAttribute)); + + return attribute.LabelText; + } + + private static void ConfigureColor(Config config) + { + Console.WriteLine("Выборите цвет из возможных:"); + var colors = Enum.GetValues(typeof(RainbowColors)) + .Cast() + .ToDictionary(color => GetLabel(color).ToLower(), color => color); + + foreach (var color in colors) + Console.WriteLine("\t" + color.Key); + + Console.WriteLine("В случае неправильного ввода - цвет будет выбираться случайным образом"); + var inp = Console.ReadLine().ToLower(); + if (colors.TryGetValue(inp, out var colorName)) + { + config.Color = Color.FromName(colorName.ToString()); + Console.WriteLine($"Выбран {inp} цвет"); + } + else + Console.WriteLine("Цвет будет выбираться случайно"); + + } + + private static void ConfigureCloudView(Config config) + { + Console.WriteLine("Выберите внешний вид облака из возможных:"); + var pointGenerators = FindImplemetations(); + foreach (var point in pointGenerators) + Console.WriteLine("\t" + point.Key); + Console.WriteLine("Введите, соблюдая орфографию"); + var pointGenerator = Console.ReadLine().ToLower(); + if (pointGenerators.TryGetValue(pointGenerator, out var pointGeneratorName)) + config.PointGenerator = pointGeneratorName; + else + { + Console.WriteLine("Такой формы не предусмотрено"); + ConfigureCloudView(config); + } + } + + private static Dictionary FindImplemetations() + { + var assembly = Assembly.LoadFrom("TagsCloudContainer.dll"); + var type = typeof(T); + return assembly.GetTypes() + .Where(t => type.IsAssignableFrom(t) && !t.IsInterface) + .ToDictionary(x => x.GetCustomAttribute().LabelText.ToLower(), x => x); + } + + private static string? ReadValue(string? argName = null) + { + Console.Write($"{argName ?? ""}: "); + return Console.ReadLine(); + } + } +} diff --git a/Client/TestFile.txt b/Client/TestFile.txt new file mode 100644 index 00000000..b72e73dd --- /dev/null +++ b/Client/TestFile.txt @@ -0,0 +1,2 @@ +корзина корзина корзина корзина фрукты фрукты фрукты овощи овощи овощи яблоки груши бананы смородина персики картофель свекла +морковь он я ты она за по \ No newline at end of file diff --git a/Client/test.doc b/Client/test.doc new file mode 100644 index 00000000..ff5bcc6f Binary files /dev/null and b/Client/test.doc differ diff --git a/Client/test.docx b/Client/test.docx new file mode 100644 index 00000000..e504defc Binary files /dev/null and b/Client/test.docx differ diff --git a/TagsCloudContainer.Tests/CloudLayoutShould.cs b/TagsCloudContainer.Tests/CloudLayoutShould.cs new file mode 100644 index 00000000..c99dd167 --- /dev/null +++ b/TagsCloudContainer.Tests/CloudLayoutShould.cs @@ -0,0 +1,45 @@ +using FluentAssertions; +using System.Drawing; +using TagsCloudContainer.PointGenerators; + + +namespace TagsCloudContainer.Tests +{ + [TestFixture] + public class CloudLayoutShould + { + [TestCase(1, 2, TestName = "Odd coordinate value results in an even size value")] + [TestCase(2, 5, TestName = "Even coordinate value results in an odd size value")] + public void MakeRightSizeLayout(int coordinateValue, int sizeValue) + { + var center = new Point(coordinateValue, coordinateValue); + var size = new Size(sizeValue, sizeValue); + + var layout = new CloudLayout(center, new ArchemedianSpiral()); + + layout.Size.Should().BeEquivalentTo(size); + } + + [TestCase(-1, 1, TestName = "Negative X")] + [TestCase(1, -1, TestName = "Negative Y")] + [TestCase(0, 1, TestName = "Zero X")] + [TestCase(1, 0, TestName = "Zero Y")] + public void GetOnlyPositiveCenterCoordinates(int x, int y) + { + Action makeLayout = () => new CloudLayout(new Point(x, y), new ArchemedianSpiral()); + + makeLayout.Should().Throw() + .WithMessage("Center coordinates values have to be greater than Zero"); + } + + [Test] + public void PutNextRectangle_ShouldKeepEnteredSize() + { + var layout = new CloudLayout(new Point(5, 5), new ArchemedianSpiral()); + var enteredSize = new Size(3, 4); + var returnedSize = layout.PutNextRectangle(enteredSize).Size; + + returnedSize.Should().BeEquivalentTo(enteredSize); + } + } +} diff --git a/TagsCloudContainer.Tests/IPointGeneratorShould.cs b/TagsCloudContainer.Tests/IPointGeneratorShould.cs new file mode 100644 index 00000000..7d0b0061 --- /dev/null +++ b/TagsCloudContainer.Tests/IPointGeneratorShould.cs @@ -0,0 +1,44 @@ +using FluentAssertions; +using System.Drawing; +using TagsCloudContainer.PointGenerators; + +namespace TagsCloudContainer.Tests +{ + [TestFixture] + public class IPointGeneratorShould + { + [TestCaseSource(nameof(TestCases))] + public void GeneratePoints_MovingAwayFromTheStartFor(IPointGenerator pointGenerator) + { + var start = new Point(0, 0); + var points = pointGenerator.GeneratePoints(start); + var nearPoint = points.ElementAt(100); + var farPoint = points.ElementAt(1000); + + DistanceBetween(start, nearPoint).Should().BeLessThan(DistanceBetween(start, farPoint)); + } + + [TestCaseSource(nameof(TestCases))] + public void GeneratePoints_ReturnsStartAsFirstPointFor(IPointGenerator pointGenerator) + { + var start = new Point(100, 100); + var firstReturned = pointGenerator.GeneratePoints(start) + .First(); + + firstReturned.Should().BeEquivalentTo(start); + } + + private static IEnumerable TestCases() + { + yield return new ArchemedianSpiral(); + yield return new HeartShaped(); + yield return new DeltaShaped(); + } + + private static int DistanceBetween(Point start, Point destination) + { + return (int)Math.Sqrt((start.X - destination.X) * (start.X - destination.X) + + (start.Y - destination.Y) * (start.Y - destination.Y)); + } + } +} diff --git a/TagsCloudContainer.Tests/TagGeneratorShould.cs b/TagsCloudContainer.Tests/TagGeneratorShould.cs new file mode 100644 index 00000000..3ab47e91 --- /dev/null +++ b/TagsCloudContainer.Tests/TagGeneratorShould.cs @@ -0,0 +1,24 @@ +using FluentAssertions; +using TagsCloudContainer.ColorProviders; +using TagsCloudContainer.StringParsers; +using TagsCloudContainer.TextProviders; +using TagsCloudContainer.WordFilters; + +namespace TagsCloudContainer.Tests +{ + public class TagGeneratorShould + { + [Test] + public void SetRightFontSize() + { + var processor = new TextProcessor.TextProcessor( + new TxtTextProvider(@"TextFile1.txt"), new RegexParser(), new ToLowerFilter(), new BoringWordFilter()); + var words = processor.WordFrequencies(); + var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new System.Drawing.Font("arial", 12)); + var result = generator.GenerateTags(words).First(); + + result.Font.Name.Should().Be("Arial"); + result.Font.Size.Should().Be(36); + } + } +} diff --git a/TagsCloudContainer.Tests/TagsCloudContainer.Tests.csproj b/TagsCloudContainer.Tests/TagsCloudContainer.Tests.csproj new file mode 100644 index 00000000..6ff8d572 --- /dev/null +++ b/TagsCloudContainer.Tests/TagsCloudContainer.Tests.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/TagsCloudContainer.Tests/TextFile1.txt b/TagsCloudContainer.Tests/TextFile1.txt new file mode 100644 index 00000000..4bd0f5da --- /dev/null +++ b/TagsCloudContainer.Tests/TextFile1.txt @@ -0,0 +1 @@ +он ОНА ОНО корзина творог печенье Корзина ++ ,- = корЗина по ха за \ No newline at end of file diff --git a/TagsCloudContainer.Tests/TextProcessorShould.cs b/TagsCloudContainer.Tests/TextProcessorShould.cs new file mode 100644 index 00000000..07a5cfa9 --- /dev/null +++ b/TagsCloudContainer.Tests/TextProcessorShould.cs @@ -0,0 +1,21 @@ +using FluentAssertions; +using TagsCloudContainer.StringParsers; +using TagsCloudContainer.TextProviders; +using TagsCloudContainer.WordFilters; + +namespace TagsCloudContainer.Tests +{ + public class TextProcessorShould + { + [Test] + public void Process() + { + var result = new TextProcessor.TextProcessor( + new TxtTextProvider(@"TextFile1.txt"), new RegexParser(), new ToLowerFilter(), new BoringWordFilter(), new ShortWordFilter()).WordFrequencies(); + + result.Count.Should().Be(3); + + result.MaxBy(word => word.Value).Value.Should().Be(3); + } + } +} diff --git a/TagsCloudContainer.Tests/TxtTextProviderShould.cs b/TagsCloudContainer.Tests/TxtTextProviderShould.cs new file mode 100644 index 00000000..461f2818 --- /dev/null +++ b/TagsCloudContainer.Tests/TxtTextProviderShould.cs @@ -0,0 +1,23 @@ +using FluentAssertions; +using TagsCloudContainer.TextProviders; + +namespace TagsCloudContainer.Tests +{ + public class TxtTextProviderShould + { + private TxtTextProvider _provider; + [SetUp] + public void Setup() + { + _provider = new TxtTextProvider("NotExisted.txt"); + } + + [Test] + public void ThrowExceptionIfFileNotFounded() + { + Action act = () => _provider.ReadFile(); + + act.Should().Throw(); + } + } +} \ No newline at end of file diff --git a/TagsCloudContainer/CloudLayout.cs b/TagsCloudContainer/CloudLayout.cs new file mode 100644 index 00000000..bcc26d51 --- /dev/null +++ b/TagsCloudContainer/CloudLayout.cs @@ -0,0 +1,65 @@ +using System.Drawing; +using TagsCloudContainer.PointGenerators; + +namespace TagsCloudContainer +{ + public class CloudLayout + { + private readonly Point Center; + public readonly Size Size; + private readonly IEnumerable _points; + private List Rectangles { get; set; } + + + public CloudLayout(Point center, IPointGenerator pointGenerator) + { + if (center.X <= 0 || center.Y <= 0) + throw new ArgumentException("Center coordinates values have to be greater than Zero"); + Center = center; + Size = CountSize(center); + Rectangles = []; + _points = pointGenerator.GeneratePoints(Center); + } + + public CloudLayout(Size size, IPointGenerator pointGenerator) + { + Size = size; + Center = FindCenter(size); + Rectangles = []; + _points = pointGenerator.GeneratePoints(Center); + } + + + private Size CountSize(Point center) + { + var width = (center.X % 2 == 0) ? center.X * 2 + 1 : Center.X * 2; + var height = (center.Y % 2 == 0) ? center.Y * 2 + 1 : center.Y * 2; + return new Size(width, height); + } + + private static Point FindCenter(Size size) + { + return new Point(size.Width / 2, size.Height / 2); + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + foreach (var point in _points) + { + var supposed = new Rectangle(new Point(point.X - rectangleSize.Width / 2, point.Y - rectangleSize.Height / 2), + rectangleSize); + if (IntersectsWithAnyOther(supposed, Rectangles)) + continue; + Rectangles.Add(supposed); + return supposed; + } + throw new ArgumentException("Not Enough Points Generated"); + } + + public static bool IntersectsWithAnyOther(Rectangle supposed, List others) + { + return others.Any(x => x.IntersectsWith(supposed)); + } + } +} + diff --git a/TagsCloudContainer/ColorProviders/ColorProvider.cs b/TagsCloudContainer/ColorProviders/ColorProvider.cs new file mode 100644 index 00000000..07b3e61e --- /dev/null +++ b/TagsCloudContainer/ColorProviders/ColorProvider.cs @@ -0,0 +1,13 @@ +using System.Drawing; +using System.Runtime.CompilerServices; + +namespace TagsCloudContainer.ColorProviders; + +public class ColorProvider : IColorProvider +{ + [CompilerGenerated] private readonly Color _color; + + public ColorProvider(Color color) => _color = color; + + public Color GetColor() => _color; +} \ No newline at end of file diff --git a/TagsCloudContainer/ColorProviders/IColorProvider.cs b/TagsCloudContainer/ColorProviders/IColorProvider.cs new file mode 100644 index 00000000..682ceaa1 --- /dev/null +++ b/TagsCloudContainer/ColorProviders/IColorProvider.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloudContainer.ColorProviders; + +public interface IColorProvider +{ + Color GetColor(); +} \ No newline at end of file diff --git a/TagsCloudContainer/ColorProviders/RandomColorProvider.cs b/TagsCloudContainer/ColorProviders/RandomColorProvider.cs new file mode 100644 index 00000000..2d186faa --- /dev/null +++ b/TagsCloudContainer/ColorProviders/RandomColorProvider.cs @@ -0,0 +1,11 @@ +using System.Drawing; + +namespace TagsCloudContainer.ColorProviders; + +public class RandomColorProvider : IColorProvider +{ + public Color GetColor() + { + return Color.FromArgb(Random.Shared.Next(50, 255), Random.Shared.Next(0, 255), Random.Shared.Next(0, 255), Random.Shared.Next(0, 255)); + } +} \ No newline at end of file diff --git a/TagsCloudContainer/Configuration/Config.cs b/TagsCloudContainer/Configuration/Config.cs new file mode 100644 index 00000000..664ab43f --- /dev/null +++ b/TagsCloudContainer/Configuration/Config.cs @@ -0,0 +1,21 @@ +using System.Drawing; + +namespace TagsCloudContainer.Configuration; + +public class Config +{ + public Type PointGenerator { get; set; } + + public Color? Color { get; set; } + + public string FilePath { get; set; } + + public string PicturePath { get; set; } + + public Point StartPoint { get; set; } + + public Font Font { get; set; } + + public Dictionary SupportedReadingFormats { get; set; } + +} \ No newline at end of file diff --git a/TagsCloudContainer/LabelAttribute.cs b/TagsCloudContainer/LabelAttribute.cs new file mode 100644 index 00000000..263149f8 --- /dev/null +++ b/TagsCloudContainer/LabelAttribute.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer +{ + public class LabelAttribute(string labelText) : Attribute + { + public string LabelText { get; set; } = labelText; + } +} diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs new file mode 100644 index 00000000..1b58c696 --- /dev/null +++ b/TagsCloudContainer/PictureMaker.cs @@ -0,0 +1,42 @@ +using System.Drawing; +using TagsCloudContainer.PointGenerators; +using TagsCloudContainer.TagGenerator; +using TagsCloudContainer.TextProcessor; + +namespace TagsCloudContainer; + +public class PictureMaker +{ + private readonly IPointGenerator _pointGenerator; + private readonly IEnumerable _tags; + private readonly string _fileName; + private readonly Point _startPoint; + + public PictureMaker(IPointGenerator pointGenerator, ITagsGenerator tagGenerator, + ITextProcessor textProcessor, string fileName, Point startPoint) + { + _pointGenerator = pointGenerator; + _tags = tagGenerator.GenerateTags(textProcessor.WordFrequencies()); + _fileName = fileName; + _startPoint = startPoint; + } + + public void DrawPicture() + { + var layout = new CloudLayout(_startPoint, _pointGenerator); + using var image = new Bitmap(layout.Size.Width, layout.Size.Height); + foreach (var tag in _tags) + { + var rectangle = layout.PutNextRectangle(tag.Frame); + DrawTag(image, rectangle, tag); + } + image.Save(_fileName); + } + + private static void DrawTag(Bitmap image, Rectangle rectangle, Tag tag) + { + using var brush = new SolidBrush(tag.Color); + using var formGraphics = Graphics.FromImage(image); + formGraphics.DrawString(tag.Word.Value, tag.Font, brush, rectangle.Location); + } +} \ No newline at end of file diff --git a/TagsCloudContainer/PointGenerators/ArchemedianSpiral.cs b/TagsCloudContainer/PointGenerators/ArchemedianSpiral.cs new file mode 100644 index 00000000..a942a18b --- /dev/null +++ b/TagsCloudContainer/PointGenerators/ArchemedianSpiral.cs @@ -0,0 +1,23 @@ +using System.Drawing; + +namespace TagsCloudContainer.PointGenerators +{ + [Label("Спираль")] + public class ArchemedianSpiral : IPointGenerator + { + public IEnumerable GeneratePoints(Point start) + { + var zoom = 1; + var spiralStep = 0.0; + yield return start; + while (true) + { + spiralStep += Math.PI / 180; + var x = start.X + (int)(zoom * spiralStep * Math.Cos(spiralStep)); + var y = start.Y + (int)(zoom * spiralStep * Math.Sin(spiralStep)); + var next = new Point(x, y); + yield return next; + } + } + } +} diff --git a/TagsCloudContainer/PointGenerators/DeltaShaped.cs b/TagsCloudContainer/PointGenerators/DeltaShaped.cs new file mode 100644 index 00000000..5e54e480 --- /dev/null +++ b/TagsCloudContainer/PointGenerators/DeltaShaped.cs @@ -0,0 +1,35 @@ +using System.Drawing; + +namespace TagsCloudContainer.PointGenerators +{ + [Label("Стринги")] + public class DeltaShaped : IPointGenerator + { + public IEnumerable GeneratePoints(Point start) + { + var zoom = 5; + yield return start; + while (true) + { + foreach (var pair in Delta()) + { + var x = start.X + (int)(zoom * pair.Item1); + var y = start.Y + (int)(zoom * pair.Item2); + var next = new Point(x, y); + yield return next; + } + zoom += 2; + } + } + + public static IEnumerable<(double, double)> Delta() + { + for (var t = 0.0; t < 2 * Math.PI; t += Math.PI / 180) + { + var x = 2 * Math.Cos(t) + Math.Cos(2 * t); + var y = 2 * Math.Sin(t) - Math.Sin(2 * t); + yield return (x, y); + } + } + } +} diff --git a/TagsCloudContainer/PointGenerators/HeartShaped.cs b/TagsCloudContainer/PointGenerators/HeartShaped.cs new file mode 100644 index 00000000..6c09c918 --- /dev/null +++ b/TagsCloudContainer/PointGenerators/HeartShaped.cs @@ -0,0 +1,35 @@ +using System.Drawing; + +namespace TagsCloudContainer.PointGenerators +{ + [Label("Сердце")] + public class HeartShaped : IPointGenerator + { + public IEnumerable GeneratePoints(Point start) + { + var zoom = 1; + yield return start; + while (true) + { + foreach (var pair in Heart()) + { + var x = start.X + (int)(zoom * pair.Item1); + var y = start.Y + (int)(zoom * pair.Item2); + var next = new Point(x, y); + yield return next; + } + zoom += 1; + } + } + + public static IEnumerable<(double, double)> Heart() + { + for (var t = 0.0; t < 2 * Math.PI; t += Math.PI / 180) + { + var x = 16 * Math.Sin(t) * Math.Sin(t) * Math.Sin(t); + var y = -13 * Math.Cos(t) + 5 * Math.Cos(2 * t) + 2 * Math.Cos(3 * t) + Math.Cos(4 * t); + yield return (x, y); + } + } + } +} diff --git a/TagsCloudContainer/PointGenerators/IPointGenerator.cs b/TagsCloudContainer/PointGenerators/IPointGenerator.cs new file mode 100644 index 00000000..ad4ad966 --- /dev/null +++ b/TagsCloudContainer/PointGenerators/IPointGenerator.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagsCloudContainer.PointGenerators +{ + public interface IPointGenerator + { + IEnumerable GeneratePoints(Point start); + } +} diff --git a/TagsCloudContainer/RainbowColors.cs b/TagsCloudContainer/RainbowColors.cs new file mode 100644 index 00000000..ace69583 --- /dev/null +++ b/TagsCloudContainer/RainbowColors.cs @@ -0,0 +1,20 @@ +namespace TagsCloudContainer +{ + public enum RainbowColors + { + [Label("Красный")] + Red, + [Label("Оранжевый")] + Orange, + [Label("Желтый")] + Yellow, + [Label("Зеленый")] + Green, + [Label("Голубой")] + Blue, + [Label("Синий")] + Indigo, + [Label("Фиолетовый")] + Violet, + } +} diff --git a/TagsCloudContainer/StringParsers/IStringParser.cs b/TagsCloudContainer/StringParsers/IStringParser.cs new file mode 100644 index 00000000..f85d252c --- /dev/null +++ b/TagsCloudContainer/StringParsers/IStringParser.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer.StringParsers +{ + public interface IStringParser + { + IEnumerable GetWordsFromString(string input); + } +} diff --git a/TagsCloudContainer/StringParsers/RegexParser.cs b/TagsCloudContainer/StringParsers/RegexParser.cs new file mode 100644 index 00000000..9887a74f --- /dev/null +++ b/TagsCloudContainer/StringParsers/RegexParser.cs @@ -0,0 +1,15 @@ +using System.Text.RegularExpressions; + +namespace TagsCloudContainer.StringParsers +{ + public class RegexParser : IStringParser + { + private readonly Regex _regex = new("\\b(?:\\w|-)+\\b", RegexOptions.Compiled); + public IEnumerable GetWordsFromString(string input) + { + return _regex.Matches(input) + .Cast() + .Select(w => new Word(w.Value)); + } + } +} diff --git a/TagsCloudContainer/Tag.cs b/TagsCloudContainer/Tag.cs new file mode 100644 index 00000000..2dd79ff4 --- /dev/null +++ b/TagsCloudContainer/Tag.cs @@ -0,0 +1,5 @@ +using System.Drawing; + +namespace TagsCloudContainer; + +public record Tag(Word Word, Font Font, Color Color, Size Frame); diff --git a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs new file mode 100644 index 00000000..421c4f02 --- /dev/null +++ b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer.TagGenerator +{ + public interface ITagsGenerator + { + IEnumerable GenerateTags(Dictionary wordsDictionary); + } +} diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs new file mode 100644 index 00000000..dff3a334 --- /dev/null +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -0,0 +1,38 @@ +using System.Drawing; +using TagsCloudContainer.ColorProviders; + +namespace TagsCloudContainer.TagGenerator +{ + public class TagGenerator : ITagsGenerator + { + private readonly IColorProvider _colorProvider; + private readonly Graphics _graphics; + private readonly Font _defaultFont; + + + public TagGenerator(IColorProvider colorProvider, Font defaultFont ) + { + _colorProvider = colorProvider; + _graphics = Graphics.FromImage(new Bitmap(1, 1)); + _defaultFont = defaultFont; + } + + public IEnumerable GenerateTags(Dictionary wordsDictionary) + { + return wordsDictionary + .Select(kvp => new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), _colorProvider.GetColor(), + SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); + } + + private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics graphics) + { + var rect = graphics.MeasureString(word.Value, font).ToSize(); + return new Size(rect.Width + frameGap, rect.Height + frameGap); + } + + private static Font SetFont(Font font, int amount) + { + return new Font(font.FontFamily, font.Size * amount); + } + } +} diff --git a/TagsCloudContainer/TagsCloudContainer.csproj b/TagsCloudContainer/TagsCloudContainer.csproj new file mode 100644 index 00000000..e35483a8 --- /dev/null +++ b/TagsCloudContainer/TagsCloudContainer.csproj @@ -0,0 +1,16 @@ + + + + Library + net8.0 + enable + enable + + + + + + + + + diff --git a/TagsCloudContainer/TagsCloudContainer.sln b/TagsCloudContainer/TagsCloudContainer.sln new file mode 100644 index 00000000..8274b4ed --- /dev/null +++ b/TagsCloudContainer/TagsCloudContainer.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagsCloudContainer", "TagsCloudContainer.csproj", "{9A86D0EB-2E44-4B2D-A9C9-BCF4C6037DE4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagsCloudContainer.Tests", "..\TagsCloudContainer.Tests\TagsCloudContainer.Tests.csproj", "{A6199F1E-60D0-4BC3-8A43-F0E8D0A8F7E7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "..\Client\Client.csproj", "{D35DD45C-AA81-4D59-BA31-261C006DB97E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9A86D0EB-2E44-4B2D-A9C9-BCF4C6037DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A86D0EB-2E44-4B2D-A9C9-BCF4C6037DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A86D0EB-2E44-4B2D-A9C9-BCF4C6037DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A86D0EB-2E44-4B2D-A9C9-BCF4C6037DE4}.Release|Any CPU.Build.0 = Release|Any CPU + {A6199F1E-60D0-4BC3-8A43-F0E8D0A8F7E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6199F1E-60D0-4BC3-8A43-F0E8D0A8F7E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6199F1E-60D0-4BC3-8A43-F0E8D0A8F7E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6199F1E-60D0-4BC3-8A43-F0E8D0A8F7E7}.Release|Any CPU.Build.0 = Release|Any CPU + {D35DD45C-AA81-4D59-BA31-261C006DB97E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D35DD45C-AA81-4D59-BA31-261C006DB97E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D35DD45C-AA81-4D59-BA31-261C006DB97E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D35DD45C-AA81-4D59-BA31-261C006DB97E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {37859005-F9FC-4CB8-81DF-92A65CC08048} + EndGlobalSection +EndGlobal diff --git a/TagsCloudContainer/TextProcessor/ITextProcessor.cs b/TagsCloudContainer/TextProcessor/ITextProcessor.cs new file mode 100644 index 00000000..b22fb0fd --- /dev/null +++ b/TagsCloudContainer/TextProcessor/ITextProcessor.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer.TextProcessor +{ + public interface ITextProcessor + { + public Dictionary WordFrequencies(); + } +} diff --git a/TagsCloudContainer/TextProcessor/TextProcessor.cs b/TagsCloudContainer/TextProcessor/TextProcessor.cs new file mode 100644 index 00000000..03f2fe10 --- /dev/null +++ b/TagsCloudContainer/TextProcessor/TextProcessor.cs @@ -0,0 +1,17 @@ +using TagsCloudContainer.StringParsers; +using TagsCloudContainer.TextProviders; +using TagsCloudContainer.WordFilters; + +namespace TagsCloudContainer.TextProcessor; + +public class TextProcessor(ITextProvider provider, IStringParser parser, + params IWordFilter[] filters) : ITextProcessor +{ + public Dictionary WordFrequencies() + { + var words = parser.GetWordsFromString(provider.ReadFile()); + return filters.Aggregate(words, (current, filter) => filter.Process(current)) + .GroupBy(word => word) + .ToDictionary(group => group.Key, group => group.Count()); + } +} \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/DocTextProvider.cs b/TagsCloudContainer/TextProviders/DocTextProvider.cs new file mode 100644 index 00000000..cfdd9cc1 --- /dev/null +++ b/TagsCloudContainer/TextProviders/DocTextProvider.cs @@ -0,0 +1,24 @@ +using NPOI.HWPF; + +namespace TagsCloudContainer.TextProviders; + +[Label(".doc")] +public class DocTextProvider : ITextProvider +{ + private readonly string _filePath; + + public DocTextProvider(string filePath) + { + _filePath = filePath; + } + + public string ReadFile() + { + if (!File.Exists(_filePath)) + throw new FileNotFoundException(); + using var stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read); + var document = new HWPFDocument(stream); + var range = document.GetRange(); + return range.Text; + } +} \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/DocXTextProvider.cs b/TagsCloudContainer/TextProviders/DocXTextProvider.cs new file mode 100644 index 00000000..e4e83b22 --- /dev/null +++ b/TagsCloudContainer/TextProviders/DocXTextProvider.cs @@ -0,0 +1,22 @@ +using Xceed.Words.NET; + +namespace TagsCloudContainer.TextProviders; + +[Label(".docx")] +public class DocXTextProvider : ITextProvider +{ + private readonly string _filePath; + + public DocXTextProvider(string filePath) + { + _filePath = filePath; + } + + public string ReadFile() + { + if (!File.Exists(_filePath)) + throw new FileNotFoundException(); + using var document = DocX.Load(_filePath); + return document.Text; + } +} \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/ITextProvider.cs b/TagsCloudContainer/TextProviders/ITextProvider.cs new file mode 100644 index 00000000..cb7a29b5 --- /dev/null +++ b/TagsCloudContainer/TextProviders/ITextProvider.cs @@ -0,0 +1,6 @@ +namespace TagsCloudContainer.TextProviders; + +public interface ITextProvider +{ + public string ReadFile(); +} \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/TxtTextProvider.cs b/TagsCloudContainer/TextProviders/TxtTextProvider.cs new file mode 100644 index 00000000..1d774fb6 --- /dev/null +++ b/TagsCloudContainer/TextProviders/TxtTextProvider.cs @@ -0,0 +1,19 @@ +namespace TagsCloudContainer.TextProviders; + +[Label(".txt")] +public class TxtTextProvider : ITextProvider +{ + private readonly string _filePath; + + public TxtTextProvider(string filePath) + { + _filePath = filePath; + } + + public string ReadFile() + { + if (!File.Exists(_filePath)) + throw new FileNotFoundException(); + return File.ReadAllText(_filePath); + } +} \ No newline at end of file diff --git a/TagsCloudContainer/Word.cs b/TagsCloudContainer/Word.cs new file mode 100644 index 00000000..45b2877b --- /dev/null +++ b/TagsCloudContainer/Word.cs @@ -0,0 +1,4 @@ +namespace TagsCloudContainer +{ + public record Word(string Value); +} diff --git a/TagsCloudContainer/WordFilters/BoringWordFilter.cs b/TagsCloudContainer/WordFilters/BoringWordFilter.cs new file mode 100644 index 00000000..30e1fd1e --- /dev/null +++ b/TagsCloudContainer/WordFilters/BoringWordFilter.cs @@ -0,0 +1,30 @@ +namespace TagsCloudContainer.WordFilters +{ + public class BoringWordFilter : IWordFilter + { + private readonly HashSet _forbiddenWords = + [ + "я", + "мы", + "он", + "она", + "оно", + "они" + ]; + + public void AddBoringWord(Word word) + { + _forbiddenWords.Add(word.Value); + } + + public void AddBoringWord(string word) + { + _forbiddenWords.Add(word); + } + + public IEnumerable Process(IEnumerable words) + { + return words.Where(w => !_forbiddenWords.Contains(w.Value)); + } + } +} diff --git a/TagsCloudContainer/WordFilters/IWordFilter.cs b/TagsCloudContainer/WordFilters/IWordFilter.cs new file mode 100644 index 00000000..e5e6bcd4 --- /dev/null +++ b/TagsCloudContainer/WordFilters/IWordFilter.cs @@ -0,0 +1,7 @@ +namespace TagsCloudContainer.WordFilters +{ + public interface IWordFilter + { + IEnumerable Process(IEnumerable words); + } +} diff --git a/TagsCloudContainer/WordFilters/ShortWordFilter.cs b/TagsCloudContainer/WordFilters/ShortWordFilter.cs new file mode 100644 index 00000000..90aa7144 --- /dev/null +++ b/TagsCloudContainer/WordFilters/ShortWordFilter.cs @@ -0,0 +1,10 @@ +namespace TagsCloudContainer.WordFilters +{ + public class ShortWordFilter : IWordFilter + { + public IEnumerable Process(IEnumerable words) + { + return words.Where(w => w.Value.Length > 2); + } + } +} diff --git a/TagsCloudContainer/WordFilters/ToLowerFilter.cs b/TagsCloudContainer/WordFilters/ToLowerFilter.cs new file mode 100644 index 00000000..9252c8d1 --- /dev/null +++ b/TagsCloudContainer/WordFilters/ToLowerFilter.cs @@ -0,0 +1,9 @@ +namespace TagsCloudContainer.WordFilters; + +public class ToLowerFilter : IWordFilter +{ + public IEnumerable Process(IEnumerable words) + { + return words.Select(w => new Word(w.Value.ToLower())); + } +} \ No newline at end of file