diff --git a/TagsCloud/App/App.cs b/TagsCloud/App/App.cs new file mode 100644 index 000000000..f5af648ba --- /dev/null +++ b/TagsCloud/App/App.cs @@ -0,0 +1,25 @@ +using TagsCloud.Layouters; +using TagsCloud.TagsCloudPainters; +using TagsCloud.WordsProviders; + +namespace TagsCloud.App; + +public class App : IApp +{ + private readonly IWordsProvider wordsProvider; + private readonly ILayouter layouter; + private readonly IPainter painter; + + public App(IWordsProvider wordsProvider, ILayouter layouter, IPainter painter) + { + this.wordsProvider = wordsProvider; + this.layouter = layouter; + this.painter = painter; + } + + public void Run() + { + layouter.CreateTagCloud(wordsProvider.GetWords()); + painter.DrawCloud(layouter.GetTagsCollection(),layouter.GetImageSize()); + } +} \ No newline at end of file diff --git a/TagsCloud/App/IApp.cs b/TagsCloud/App/IApp.cs new file mode 100644 index 000000000..bd30bab83 --- /dev/null +++ b/TagsCloud/App/IApp.cs @@ -0,0 +1,6 @@ +namespace TagsCloud.App; + +public interface IApp +{ + public void Run(); +} \ No newline at end of file diff --git a/TagsCloud/ColorGenerators/IColorGenerator.cs b/TagsCloud/ColorGenerators/IColorGenerator.cs new file mode 100644 index 000000000..5ad53c265 --- /dev/null +++ b/TagsCloud/ColorGenerators/IColorGenerator.cs @@ -0,0 +1,9 @@ +using System.Drawing; +using TagsCloud.Entities; + +namespace TagsCloud.ColorGenerators; + +public interface IColorGenerator +{ + Color GetTagColor(Tag tag); +} \ No newline at end of file diff --git a/TagsCloud/ColorGenerators/RandomColorGenerator.cs b/TagsCloud/ColorGenerators/RandomColorGenerator.cs new file mode 100644 index 000000000..a1dcc2b26 --- /dev/null +++ b/TagsCloud/ColorGenerators/RandomColorGenerator.cs @@ -0,0 +1,14 @@ +using System.Drawing; +using TagsCloud.Entities; + +namespace TagsCloud.ColorGenerators; + +public class RandomColorGenerator : IColorGenerator +{ + private static readonly Random Random = new Random(); + + public Color GetTagColor(Tag tag) + { + return Color.FromArgb(Random.Next(256), Random.Next(256), Random.Next(256)); + } +} \ No newline at end of file diff --git a/TagsCloud/ConsoleCommands/Conventers/SizeTypeConverter.cs b/TagsCloud/ConsoleCommands/Conventers/SizeTypeConverter.cs new file mode 100644 index 000000000..32ede9a51 --- /dev/null +++ b/TagsCloud/ConsoleCommands/Conventers/SizeTypeConverter.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; +using System.Drawing; + +namespace TagsCloud.ConsoleCommands.Conventers; + +public class SizeTypeConverter : TypeConverter +{ + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, + object value) + { + if (value is string stringValue) + { + string[] sizeValues = stringValue.Split(','); + + if (sizeValues.Length == 2 && + int.TryParse(sizeValues[0], out int width) && + int.TryParse(sizeValues[1], out int height)) + { + return new Size(width, height); + } + } + + throw new ArgumentException(); + } +} \ No newline at end of file diff --git a/TagsCloud/ConsoleCommands/Options.cs b/TagsCloud/ConsoleCommands/Options.cs new file mode 100644 index 000000000..c23888e22 --- /dev/null +++ b/TagsCloud/ConsoleCommands/Options.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; +using System.Drawing; +using CommandLine; +using TagsCloud.ConsoleCommands.Conventers; + +namespace TagsCloud.ConsoleCommands; + +public class Options +{ + [Option('i', "inputFile", Default = "C:\\Users\\hellw\\Desktop\\C#\\di\\TagsCloud\\source\\input.txt", + HelpText = "Set input file name.")] + public string InputFile { get; set; } + + [Option('o', "outputFile", Default = "C:\\Users\\hellw\\Desktop\\C#\\di\\TagsCloud\\source\\output.png", + HelpText = "Set output file name.")] + public string OutputFile { get; set; } + + [Option('f', "font", Default = "Arial", HelpText = "Set tagsCloud word Font")] + public string TagsFont { get; set; } + + [Option('s', "imagesize", Default = null, HelpText = "Set output image size")] + [TypeConverter(typeof(SizeTypeConverter))] + public Size ImageSize { get; set; } + + [Option('b', "background", Default = "Empty", HelpText = "Set tagsCloud output background color")] + public string Background { get; set; } + + [Option('t', "parts", Default = new string[] { "sdsd", "sfdfd" }, HelpText = "Word available parts of speech.")] + public IEnumerable PartsOfSpeech { get; set; } +} \ No newline at end of file diff --git a/TagsCloud/ContainerConfig.cs b/TagsCloud/ContainerConfig.cs new file mode 100644 index 000000000..378cde7f7 --- /dev/null +++ b/TagsCloud/ContainerConfig.cs @@ -0,0 +1,31 @@ +using Autofac; +using TagsCloud.App; +using TagsCloud.ColorGenerators; +using TagsCloud.ConsoleCommands; +using TagsCloud.Distributors; +using TagsCloud.Layouters; +using TagsCloud.TagsCloudPainters; +using TagsCloud.WordsProviders; +using TagsCloud.WordFontCalculators; +using TagsCloud.WordValidators; + + +namespace TagsCloud; + +public static class ContainerConfig +{ + public static IContainer Configure(Options options) + { + var builder = new ContainerBuilder(); + builder.RegisterInstance(options).AsSelf(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + return builder.Build(); + } +} \ No newline at end of file diff --git a/TagsCloud/Distributors/IDistributor.cs b/TagsCloud/Distributors/IDistributor.cs new file mode 100644 index 000000000..136350957 --- /dev/null +++ b/TagsCloud/Distributors/IDistributor.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloud.Distributors; + +public interface IDistributor +{ + Point GetNextPosition(); +} \ No newline at end of file diff --git a/TagsCloud/Distributors/SpiralDistributor.cs b/TagsCloud/Distributors/SpiralDistributor.cs new file mode 100644 index 000000000..ea9d0ebbe --- /dev/null +++ b/TagsCloud/Distributors/SpiralDistributor.cs @@ -0,0 +1,39 @@ +using System.Drawing; + +namespace TagsCloud.Distributors; + +public class SpiralDistributor : IDistributor +{ + public double Angle { get; private set; } + public double Radius { get; private set; } + public double AngleStep { get; private set; } + public double RadiusStep { get; private set; } + public Point Center { get; private set; } + + public SpiralDistributor(Point center = new Point(), double angleStep = 0.1, double radiusStep = 0.1) + { + if (radiusStep <= 0 || angleStep == 0) throw new ArgumentException(); + this.Center = center; + Radius = 0; + Angle = 0; + this.AngleStep = angleStep - 2 * Math.PI * (int)(angleStep / 2 * Math.PI); + this.RadiusStep = radiusStep; + } + + + public Point GetNextPosition() + { + var x = Radius * Math.Cos(Angle) + Center.X; + var y = Radius * Math.Sin(Angle) + Center.Y; + + Angle += AngleStep; + + if (Angle >= Math.PI * 2) + { + Angle -= 2 * Math.PI; + Radius += RadiusStep; + } + + return new Point((int)x, (int)y); + } +} \ No newline at end of file diff --git a/TagsCloud/Entities/Tag.cs b/TagsCloud/Entities/Tag.cs new file mode 100644 index 000000000..bc4545a03 --- /dev/null +++ b/TagsCloud/Entities/Tag.cs @@ -0,0 +1,17 @@ +using System.Drawing; + +namespace TagsCloud.Entities; + +public class Tag +{ + public Rectangle TagRectangle; + public string Content; + public Font Font; + + public Tag(Rectangle tagRectangle, Font font, string content) + { + this.Content = content; + this.TagRectangle = tagRectangle; + this.Font = font; + } +} \ No newline at end of file diff --git a/TagsCloud/Layouters/CircularCloudLayouter.cs b/TagsCloud/Layouters/CircularCloudLayouter.cs new file mode 100644 index 000000000..947ab0a91 --- /dev/null +++ b/TagsCloud/Layouters/CircularCloudLayouter.cs @@ -0,0 +1,120 @@ +using System.Drawing; +using TagsCloud.Distributors; +using TagsCloud.Entities; +using TagsCloud.WordFontCalculators; + +namespace TagsCloud.Layouters; + +public class CircularCloudLayouter : ILayouter +{ + private readonly List tags; + private IDistributor distributor; + private IWordFontCalculator fontCalculator; + public Point Center { get; private set; } + private int leftBorder; + private int rightBorder; + private int topBorder; + private int bottomBorder; + + public CircularCloudLayouter(IDistributor distributor, + IWordFontCalculator fontCalculator, + Point center = new Point()) + { + this.distributor = distributor; + this.fontCalculator = fontCalculator; + this.Center = center; + tags = new(); + } + + public void CreateTagCloud(Dictionary tagsDictionary) + { + foreach (var tag in tagsDictionary) + { + var tagFont = fontCalculator.GetWordFont(tag.Key, tag.Value); + var rectangle = new Rectangle(); + rectangle.Location = distributor.GetNextPosition(); + + using (Graphics g = Graphics.FromImage(new Bitmap(1, 1))) + { + var sizeF = g.MeasureString(tag.Key, tagFont); + rectangle.Size = new Size((int)Math.Ceiling(sizeF.Width), (int)Math.Ceiling(sizeF.Height)); + } + + while (CheckIntersection(rectangle)) + { + rectangle.Location = distributor.GetNextPosition(); + } + + rectangle = CompressRectangleToCenter(rectangle); + UpdateImageSize(rectangle); + var newtag = new Tag(rectangle, tagFont, tag.Key); + tags.Add(newtag); + } + } + + public IEnumerable GetTagsCollection() + { + return tags; + } + + public Size GetImageSize() + { + return new Size(Math.Abs(rightBorder - leftBorder), Math.Abs(topBorder - bottomBorder)); + } + + private void UpdateImageSize(Rectangle rec) + { + var right = rec.X + rec.Width / 2; + var left = rec.X - rec.Width / 2; + var top = rec.Y + rec.Height / 2; + var bottom = rec.Y - rec.Height / 2; + + rightBorder = right > rightBorder ? right : rightBorder; + leftBorder = left < leftBorder ? left : leftBorder; + topBorder = top > topBorder ? top : topBorder; + bottomBorder = bottom < bottomBorder ? bottom : bottomBorder; + } + + private bool CheckIntersection(Rectangle currentRectangle) + { + return tags.Any(rec => currentRectangle.IntersectsWith(rec.TagRectangle)); + } + + private Rectangle CompressRectangleToCenter(Rectangle rectangle) + { + var changes = 1; + while (changes > 0) + { + rectangle = CompressByAxis(rectangle, true, out changes); + rectangle = CompressByAxis(rectangle, false, out changes); + } + + return rectangle; + } + + private Rectangle CompressByAxis(Rectangle rectangle, bool isByX, out int changes) + { + changes = 0; + var stepX = rectangle.X < Center.X ? 1 : -1; + var stepY = rectangle.Y < Center.Y ? 1 : -1; + + while ((isByX && Math.Abs(rectangle.X - Center.X) > 0) || + (!isByX && Math.Abs(rectangle.Y - Center.Y) > 0)) + { + var newRectangle = isByX + ? new Rectangle(new Point(rectangle.X + stepX, rectangle.Y), rectangle.Size) + : new Rectangle(new Point(rectangle.X, rectangle.Y + stepY), rectangle.Size); + + if (!CheckIntersection(newRectangle)) + { + rectangle = newRectangle; + changes++; + continue; + } + + break; + } + + return rectangle; + } +} \ No newline at end of file diff --git a/TagsCloud/Layouters/ILayouter.cs b/TagsCloud/Layouters/ILayouter.cs new file mode 100644 index 000000000..992456de9 --- /dev/null +++ b/TagsCloud/Layouters/ILayouter.cs @@ -0,0 +1,12 @@ +using System.Drawing; +using TagsCloud.Entities; + +namespace TagsCloud.Layouters; + +public interface ILayouter +{ + public IEnumerable GetTagsCollection(); + public void CreateTagCloud(Dictionary tagsDictionary); + + public Size GetImageSize(); +} \ No newline at end of file diff --git a/TagsCloud/Program.cs b/TagsCloud/Program.cs new file mode 100644 index 000000000..a11c28ded --- /dev/null +++ b/TagsCloud/Program.cs @@ -0,0 +1,20 @@ +using Autofac; +using CommandLine; +using TagsCloud.App; +using TagsCloud.ConsoleCommands; + +namespace TagsCloud; + +public class Program +{ + static void Main(string[] args) + { + var options = CommandLine.Parser.Default.ParseArguments(args).Value; + var container = ContainerConfig.Configure(options); + + using var scope = container.BeginLifetimeScope(); + + var app = scope.Resolve(); + app.Run(); + } +} \ No newline at end of file diff --git a/TagsCloud/TagsCloud.csproj b/TagsCloud/TagsCloud.csproj new file mode 100644 index 000000000..d0863e451 --- /dev/null +++ b/TagsCloud/TagsCloud.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/TagsCloud/TagsCloudPainters/IPainter.cs b/TagsCloud/TagsCloudPainters/IPainter.cs new file mode 100644 index 000000000..8affa3cea --- /dev/null +++ b/TagsCloud/TagsCloudPainters/IPainter.cs @@ -0,0 +1,9 @@ +using System.Drawing; +using TagsCloud.Entities; + +namespace TagsCloud.TagsCloudPainters; + +public interface IPainter +{ + public void DrawCloud(IEnumerable tags, Size imageSize); +} \ No newline at end of file diff --git a/TagsCloud/TagsCloudPainters/SimplePainter.cs b/TagsCloud/TagsCloudPainters/SimplePainter.cs new file mode 100644 index 000000000..6f77a55f6 --- /dev/null +++ b/TagsCloud/TagsCloudPainters/SimplePainter.cs @@ -0,0 +1,61 @@ +using System.Drawing; +using System.Drawing.Imaging; +using TagsCloud.ColorGenerators; +using TagsCloud.ConsoleCommands; +using TagsCloud.Entities; +using TagsCloud.Layouters; + + +namespace TagsCloud.TagsCloudPainters; + +public class SimplePainter : IPainter +{ + private readonly IColorGenerator colorGenerator; + private readonly string filename; + private readonly Size imageSize; + private readonly Color backgroundColor; + + public SimplePainter(IColorGenerator colorGenerator, Options options) + { + this.colorGenerator = colorGenerator; + this.filename = options.OutputFile; + this.imageSize = options.ImageSize; + this.backgroundColor = Color.FromName(options.Background); + } + + public void DrawCloud(IEnumerable tags, Size size) + { + if (!tags.Any()) + throw new ArgumentException(); + var bitmapsize = imageSize.IsEmpty ? size : imageSize; + + using var bitmap = new Bitmap(bitmapsize.Width, bitmapsize.Height); + using var g = Graphics.FromImage(bitmap); + g.Clear(backgroundColor); + foreach (var tag in tags) + { + var color = colorGenerator.GetTagColor(tag); + var brush = new SolidBrush(color); + g.DrawString(tag.Content, tag.Font, brush, + GetTagPositionOnImage(tag.TagRectangle.Location, imageSize)); + } + + + SaveImageToFile(bitmap, filename); + } + + private Point GetTagPositionOnImage(Point position, Size size) + { + var x = position.X + size.Width / 2; + var y = position.Y + size.Height / 2; + + return new Point(x, y); + } + + private void SaveImageToFile(Bitmap bitmap, string filename) + { + var projectDirectory = Directory.GetParent(Environment.CurrentDirectory).Parent.FullName; + Console.WriteLine(projectDirectory); + bitmap.Save(filename, ImageFormat.Png); + } +} \ No newline at end of file diff --git a/TagsCloud/WordFontCalculators/IWordFontCalculator.cs b/TagsCloud/WordFontCalculators/IWordFontCalculator.cs new file mode 100644 index 000000000..6e1abf47a --- /dev/null +++ b/TagsCloud/WordFontCalculators/IWordFontCalculator.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloud.WordFontCalculators; + +public interface IWordFontCalculator +{ + public Font GetWordFont(string word, int count); +} \ No newline at end of file diff --git a/TagsCloud/WordFontCalculators/SimpleWordFontCalculator.cs b/TagsCloud/WordFontCalculators/SimpleWordFontCalculator.cs new file mode 100644 index 000000000..c8dfc9b99 --- /dev/null +++ b/TagsCloud/WordFontCalculators/SimpleWordFontCalculator.cs @@ -0,0 +1,19 @@ +using System.Drawing; +using TagsCloud.ConsoleCommands; + +namespace TagsCloud.WordFontCalculators; + +public class SimpleWordFontCalculator : IWordFontCalculator +{ + private readonly string font; + + public SimpleWordFontCalculator(Options options) + { + this.font = options.TagsFont; + } + + public Font GetWordFont(string word, int count) + { + return new Font(font, count); + } +} \ No newline at end of file diff --git a/TagsCloud/WordValidators/IWordValidator.cs b/TagsCloud/WordValidators/IWordValidator.cs new file mode 100644 index 000000000..f316983ee --- /dev/null +++ b/TagsCloud/WordValidators/IWordValidator.cs @@ -0,0 +1,6 @@ +namespace TagsCloud.WordValidators; + +public interface IWordValidator +{ + public bool IsWordValid(string word); +} \ No newline at end of file diff --git a/TagsCloud/WordValidators/SimpleWordValidator.cs b/TagsCloud/WordValidators/SimpleWordValidator.cs new file mode 100644 index 000000000..643ef0695 --- /dev/null +++ b/TagsCloud/WordValidators/SimpleWordValidator.cs @@ -0,0 +1,11 @@ +using MyStemWrapper; + +namespace TagsCloud.WordValidators; + +public class SimpleWordValidator : IWordValidator +{ + public bool IsWordValid(string word) + { + return word.Length > 3; + } +} \ No newline at end of file diff --git a/TagsCloud/WordsProviders/IWordsProvider.cs b/TagsCloud/WordsProviders/IWordsProvider.cs new file mode 100644 index 000000000..7da795263 --- /dev/null +++ b/TagsCloud/WordsProviders/IWordsProvider.cs @@ -0,0 +1,8 @@ +using TagsCloud.WordValidators; + +namespace TagsCloud.WordsProviders; + +public interface IWordsProvider +{ + public Dictionary GetWords(); +} \ No newline at end of file diff --git a/TagsCloud/WordsProviders/WordsProvider.cs b/TagsCloud/WordsProviders/WordsProvider.cs new file mode 100644 index 000000000..25e4bdfc8 --- /dev/null +++ b/TagsCloud/WordsProviders/WordsProvider.cs @@ -0,0 +1,29 @@ +using Autofac.Features.AttributeFilters; +using TagsCloud.ConsoleCommands; +using TagsCloud.WordValidators; + +namespace TagsCloud.WordsProviders; + +public class WordsProvider : IWordsProvider +{ + private readonly IWordValidator validator; + private readonly string filename; + + public WordsProvider(IWordValidator validator, Options options) + { + this.validator = validator; + this.filename = options.InputFile; + } + + public Dictionary GetWords() + { + if (!File.Exists(filename)) + throw new FileNotFoundException(filename); + return File.ReadAllText(filename) + .Split(new string[] { "\n", "\r\n" }, StringSplitOptions.None) + .GroupBy(word => word.ToLower()) + .Where(g => validator.IsWordValid(g.Key)) + .OrderByDescending(g2 => g2.Count()) + .ToDictionary(group => group.Key, group => group.Count()); + } +} \ No newline at end of file diff --git a/TagsCloud/source/input.txt b/TagsCloud/source/input.txt new file mode 100644 index 000000000..52d392258 --- /dev/null +++ b/TagsCloud/source/input.txt @@ -0,0 +1,243 @@ +Авиатор +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Библиотека +Водопад +Галактика +Динозавр +Единорог +Жемчуг +Звездопад +Иглу +Звездопад +Иглу +Звездопад +Иглу +Звездопад +Иглу +Звездопад +Иглу +Звездопад +Иглу +Звездопад +Иглу +Звездопад +Иглу +Космонавт +Ландыш +Магнит +Невидимка +Оркестр +Пижама +Радуга +Единорог +Единорог +Жемчуг +Звездопад +Иглу +Космонавт +Ландыш +Магнит +Невидимка +Оркестр +Пижама +Радуга +Единорог +Жемчуг +Звездопад +Иглу +Космонавт +Ландыш +Магнит +Невидимка +Оркестр +Пижама +Радуга +Жемчуг +Звездопад +Иглу +Космонавт +Ландыш +Магнит +Невидимка +Оркестр +Пижама +Радуга +Секретарь +Тропический +Ультразвук +Фея +Холодильник +Цирконий +Чудовище +Шоколад +Эксперимент +Ярмарка +Апельсин +Брошюра +Вихрь +Ярмарка +Апельсин +Брошюра +Вихрь +Ярмарка +Апельсин +Брошюра +Вихрь +Ярмарка +Апельсин +Брошюра +Вихрь +Ярмарка +Апельсин +Брошюра +Вихрь +Ярмарка +Апельсин +Брошюра +Вихрь +Ярмарка +Апельсин +Брошюра +Вихрь +Гармония +Джаз +Евкалипт +Жираф +Зебра +Инженер +Кварц +Ласточка +Мельница +Кварц +Ласточка +Мельница +Кварц +Ласточка +Мельница +Кварц +Ласточка +Мельница +Кварц +Ласточка +Мельница +Кварц +Ласточка +Мельница +Носорог +Оптимизм +Парашют +Ракета +Секатор +Тираннозавр +Утюг +Фонтан +Хамелеон +Цветочек +Черепаха +Шампунь +Экзотика +Янтарь +Арбуз +Бульканье +Волшебник +Гирлянда +Дракон +Египет +Жемчужина +Зонтик +Игрушка +Конфета +Лавина +Мандарин +Новатор +Оратор +Полет +Радио +Спутник +Торнадо +Ушанка +Футболка +Холст +Цикада +Человек +Шахматы +Этюд +Ягода +Аквариум +Берег +Волшебство +Гоблин +Динамит +Ель +Железо +Зебра +Ирис +Колесо +Лимонад +Мороженое +Носки +Овация +Пингвин +Ракурс +Спираль +Тролль +Ураган +Фантазия +Хамелеон +Цилиндр +Ирис +Колесо +Лимонад +Мороженое +Носки +Овация +Пингвин +Ракурс +Спираль +Тролль +Ураган +Фантазия +Хамелеон +Цилиндр +Ирис +Колесо +Лимонад +Мороженое +Носки +Овация +Пингвин +Ракурс +Спираль +Тролль +Ураган +Фантазия +Хамелеон +Цилиндр +Цилиндр +Ирис +Колесо +Лимонад +Цилиндр +Ирис +Колесо +Лимонад +Цилиндр +Ирис +Колесо +Лимонад + diff --git a/TagsCloud/source/output.png b/TagsCloud/source/output.png new file mode 100644 index 000000000..30c444ff3 Binary files /dev/null and b/TagsCloud/source/output.png differ diff --git a/TagsCloudTests/Distributors/SpiralDistributorTests.cs b/TagsCloudTests/Distributors/SpiralDistributorTests.cs new file mode 100644 index 000000000..5279d1777 --- /dev/null +++ b/TagsCloudTests/Distributors/SpiralDistributorTests.cs @@ -0,0 +1,70 @@ +using System.Drawing; +using FluentAssertions; +using TagsCloud.Distributors; + +namespace TagsCloudTests.Distributors; + +[TestFixture] +public class SpiralDistributorTests +{ + [SetUp] + public void SetUp() + { + center = new Point(); + spiralDistribution = new SpiralDistributor(); + } + + private Point center; + private SpiralDistributor spiralDistribution; + + + [Test] + public void SprialDistributor_InitializeDefaultParams() + { + spiralDistribution.Center.Should().Be(center); + spiralDistribution.Angle.Should().Be(0); + spiralDistribution.Radius.Should().Be(0); + spiralDistribution.AngleStep.Should().Be(0.1); + spiralDistribution.RadiusStep.Should().Be(0.1); + } + + [Test] + public void SprialDistribution_InitializeCustomParams() + { + var customCenter = new Point(1, 1); + var customSpiralDistribution = new SpiralDistributor(customCenter, 0.6, 0.7); + + customSpiralDistribution.Center.Should().Be(customCenter); + customSpiralDistribution.AngleStep.Should().Be(0.6); + customSpiralDistribution.RadiusStep.Should().Be(0.7); + } + + [TestCase(0, 1, TestName = "When_AngleStep_Is_Zero")] + [TestCase(0.6, -1, TestName = "When_Radius_Is_Negative")] + [TestCase(0.6, 0, TestName = "When_Radius_Is_Zero")] + public void SpiralDistributionConstructor_ShouldThrowArgumentException(double angleStep, double radiusStep) + { + Assert.Throws(() => new SpiralDistributor(center, angleStep, radiusStep)); + } + + [Test] + public void SpiralDistribution_ShouldReturnCenter_WhenFirstCallGetNextPoint() + { + spiralDistribution.GetNextPosition().Should().Be(center); + } + + [Test] + public void SpiralDistribution_ShouldIncreaseAngle_WhenCallGetNextPoint() + { + spiralDistribution.GetNextPosition(); + spiralDistribution.Angle.Should().Be(spiralDistribution.AngleStep); + } + + [Test] + public void SpiralDistribution_ShouldIncreaseRadius_WhenAngleMoreThan2Pi() + { + var expectedAngle = spiralDistribution.Angle * 64 - 2 * Math.PI; + for (var i = 0; i < 63; i++) spiralDistribution.GetNextPosition(); + spiralDistribution.Radius.Should().Be(spiralDistribution.Radius); + } +} \ No newline at end of file diff --git a/TagsCloudTests/Layouters/CircularCloudLayouterTests.cs b/TagsCloudTests/Layouters/CircularCloudLayouterTests.cs new file mode 100644 index 000000000..5676dd80e --- /dev/null +++ b/TagsCloudTests/Layouters/CircularCloudLayouterTests.cs @@ -0,0 +1,82 @@ +using System.Drawing; +using FluentAssertions; +using TagsCloud.ConsoleCommands; +using TagsCloud.Distributors; +using TagsCloud.Layouters; +using TagsCloud.WordFontCalculators; + +namespace TagsCloudTests.Layouters; + +public class CircularLayouterTests +{ + [TestFixture] + public class CircularCloudLayouterTests + { + [SetUp] + public void SetUp() + { + center = new Point(); + distributor = new SpiralDistributor(center); + var options = new Options { TagsFont = "Arial" }; + fontCalculator = new SimpleWordFontCalculator(options); + tagsCloud = new CircularCloudLayouter(distributor, fontCalculator, center); + } + + private Point center; + private CircularCloudLayouter tagsCloud; + private IWordFontCalculator fontCalculator; + private SpiralDistributor distributor; + + [Test] + public void CircularCloudLayouter_InitializeParams() + { + tagsCloud.GetTagsCollection().Count().Should().Be(0); + tagsCloud.Center.Should().Be(center); + } + + + [Test] + public void PutNextRectangle_ShouldPlaceFirstOnCenter() + { + tagsCloud.CreateTagCloud(new Dictionary() { { "Реваванат", 3 } }); + tagsCloud.GetTagsCollection().ToArray()[0].TagRectangle.Location.Should().Be(center); + } + + [Test] + public void CircularCloudLayouter_ShouldHasNoIntersections_When1000Words() + { + tagsCloud.CreateTagCloud(GetRandomWordsDictionary()); + tagsCloud.GetTagsCollection().Any(tag1 => + tagsCloud.GetTagsCollection().Any(tag2 => + tag1.TagRectangle.IntersectsWith(tag2.TagRectangle) && tag1 != tag2)) + .Should().BeFalse(); + } + + [Test] + public void CircularCloudLayouter_ShouldBeCloseToCircle() + { + var randomDict = GetRandomWordsDictionary(); + tagsCloud.CreateTagCloud(randomDict); + tagsCloud.GetTagsCollection().All(tag => + { + var distanceToCenter = + Math.Sqrt(Math.Pow(tag.TagRectangle.X - tagsCloud.Center.X, 2) + + Math.Pow(tag.TagRectangle.Y - tagsCloud.Center.Y, 2)); + return distanceToCenter <= distributor.Radius; + }).Should().BeTrue(); + } + + + private Dictionary GetRandomWordsDictionary() + { + var random = new Random(); + var dict = new Dictionary(); + for (var i = 0; i < 1000; i++) + { + dict[$"{i}"] = random.Next(1, 100); + } + + return dict; + } + } +} \ No newline at end of file diff --git a/TagsCloudTests/Painters/SimpleCloudPainterTests.cs b/TagsCloudTests/Painters/SimpleCloudPainterTests.cs new file mode 100644 index 000000000..27ae3fe8c --- /dev/null +++ b/TagsCloudTests/Painters/SimpleCloudPainterTests.cs @@ -0,0 +1,57 @@ +using System.Drawing; +using FluentAssertions; +using Moq; +using TagsCloud.ColorGenerators; +using TagsCloud.ConsoleCommands; +using TagsCloud.Entities; +using TagsCloud.Layouters; +using TagsCloud.TagsCloudPainters; + +namespace TagsCloudTests.Painters; + +public class SimpleCloudPainterTests +{ + [SetUp] + public void SetUp() + { + var projectDirectory = Directory.GetParent(Environment.CurrentDirectory).Parent.FullName.Replace("\\bin", ""); + var options = new Options() { OutputFile = Path.Combine(projectDirectory, "Painters", "images", FileName), Background = "Empty"}; + testFilePath = options.OutputFile; + var color = new Mock(); + color.Setup(c => c.GetTagColor(It.IsAny())).Returns(Color.Black); + painter = new SimplePainter(color.Object, options); + if (File.Exists(testFilePath)) File.Delete(testFilePath); + } + + [TearDown] + public void TearDown() + { + if (File.Exists(testFilePath)) File.Delete(testFilePath); + } + + private IPainter painter; + private const string FileName = "testcreation.png"; + private string testFilePath; + + [Test] + public void CloudLayouterDrawerConstructor_ThrowsArgumentException_WhenRectanglesLengthIsZero() + { + var layouter = new Mock(); + layouter.Setup(l => l.GetTagsCollection()).Returns(new List(){}); + layouter.Setup(l => l.GetImageSize()).Returns(new Size(500, 500)); + Assert.Throws(() => painter.DrawCloud(layouter.Object.GetTagsCollection(),layouter.Object.GetImageSize())); + } + + [Test] + public void CloudLayouterDrawer_ShouldCreateImage() + { + var layouter = new Mock(); + layouter.Setup(l => l.GetTagsCollection()).Returns(new List() + { + new Tag(new Rectangle(0, 0, 5, 5), new Font("Arial", 10), "Hello") + }); + layouter.Setup(l => l.GetImageSize()).Returns(new Size(500, 500)); + painter.DrawCloud(layouter.Object.GetTagsCollection(),layouter.Object.GetImageSize()); + File.Exists(testFilePath).Should().BeTrue(); + } +} \ No newline at end of file diff --git a/TagsCloudTests/TagsCloudTests.csproj b/TagsCloudTests/TagsCloudTests.csproj new file mode 100644 index 000000000..d30872885 --- /dev/null +++ b/TagsCloudTests/TagsCloudTests.csproj @@ -0,0 +1,30 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/TagsCloudTests/Usings.cs b/TagsCloudTests/Usings.cs new file mode 100644 index 000000000..cefced496 --- /dev/null +++ b/TagsCloudTests/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/TagsCloudTests/WordFontCalculators/SimpleWordFontCalculatorTests.cs b/TagsCloudTests/WordFontCalculators/SimpleWordFontCalculatorTests.cs new file mode 100644 index 000000000..68dd05e29 --- /dev/null +++ b/TagsCloudTests/WordFontCalculators/SimpleWordFontCalculatorTests.cs @@ -0,0 +1,22 @@ +using System.Drawing; +using FluentAssertions; +using TagsCloud.ConsoleCommands; +using TagsCloud.WordFontCalculators; + +namespace TagsCloudTests.WordFontCalculators; + +[TestFixture] +public class SimpleWordFontCalculatorTests +{ + [TestCase("Hello", 4)] + [TestCase("World", 1)] + [TestCase("Work", 12)] + [TestCase("Home", 5)] + [TestCase("Big", 134)] + public void SimpleWordFontCalculator_ShouldReturnFontSizeAsWordCount(string word, int count) + { + var options = new Options() { TagsFont = "Arial" }; + var fontCalculator = new SimpleWordFontCalculator(options); + fontCalculator.GetWordFont(word, count).Should().BeEquivalentTo(new Font("Arial", count)); + } +} \ No newline at end of file diff --git a/TagsCloudTests/WordsProviders/WordsProvideTests.cs b/TagsCloudTests/WordsProviders/WordsProvideTests.cs new file mode 100644 index 000000000..c34ac97b7 --- /dev/null +++ b/TagsCloudTests/WordsProviders/WordsProvideTests.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using Moq; +using TagsCloud.ConsoleCommands; +using TagsCloud.WordsProviders; +using TagsCloud.WordValidators; + +namespace TagsCloudTests.WordsProviders; + +[TestFixture] +public class WordsProviderTests +{ + private Mock validator; + + [SetUp] + public void Setup() + { + validator = new Mock(); + validator.Setup(v => v.IsWordValid(It.IsAny())).Returns(true); + } + + + [Test] + public void WordsProvider_ShouldThrowFileNotFoundException_WhenReadTextFromIncorrectFilePath() + { + var options = new Options(); + options.InputFile = + Path.Combine(Directory.GetParent(Environment.CurrentDirectory).Parent.FullName, "input.txt"); + var textReader = new WordsProvider(validator.Object, options); + Assert.Throws(() => textReader.GetWords()); + } + + [Test] + public void WordsProvider_ShouldReadTextFromFile() + { + var options = new Options(); + options.InputFile = + Path.Combine(Directory.GetParent(Environment.CurrentDirectory).Parent.FullName.Replace("\\bin", ""), + "WordsProviders", "input.txt"); + var words = new WordsProvider(validator.Object, options).GetWords(); + var dict = new Dictionary() + { + { "ренат", 3 }, + { "привет", 4 }, + { "дом", 1 }, + { "стол", 2 } + }; + words.Should().BeEquivalentTo(dict); + } +} \ No newline at end of file diff --git a/TagsCloudTests/WordsProviders/input.txt b/TagsCloudTests/WordsProviders/input.txt new file mode 100644 index 000000000..9a0db21a7 --- /dev/null +++ b/TagsCloudTests/WordsProviders/input.txt @@ -0,0 +1,10 @@ +Ренат +Ренат +Ренат +Привет +Привет +Привет +Привет +Дом +Стол +Стол \ No newline at end of file diff --git a/di.sln b/di.sln index b27b7c05d..38196b28d 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", "{7D410245-AB50-4458-8151-5E47BF1660D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudTests", "TagsCloudTests\TagsCloudTests.csproj", "{FBD34FCA-3E5D-4CC9-B027-53A3FB1751BB}" +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 + {7D410245-AB50-4458-8151-5E47BF1660D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D410245-AB50-4458-8151-5E47BF1660D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D410245-AB50-4458-8151-5E47BF1660D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D410245-AB50-4458-8151-5E47BF1660D8}.Release|Any CPU.Build.0 = Release|Any CPU + {FBD34FCA-3E5D-4CC9-B027-53A3FB1751BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBD34FCA-3E5D-4CC9-B027-53A3FB1751BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBD34FCA-3E5D-4CC9-B027-53A3FB1751BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBD34FCA-3E5D-4CC9-B027-53A3FB1751BB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal