diff --git a/TagsCloudVisualization/BoringWords.txt b/TagsCloudVisualization/BoringWords.txt new file mode 100644 index 00000000..4a2d7b0b --- /dev/null +++ b/TagsCloudVisualization/BoringWords.txt @@ -0,0 +1,376 @@ +а +а как же +а то как же +а то нет +а толку что +а толку-то +а чего ж +а что +а что ж +а-а +а-а-а +а-а-а-а +агу +адью +ай +ай да +ай-ай-ай +ай-люли +ай-я-яй +ай-яй-яй +айда +айда-те +айдате +аксиос +алаверды +алё +алле +алле-гоп +аллилуйя +алло +ам +ам-ам +амба +аминь +анкор +ап +апорт +ариведерчи +асеньки +асса +ась +атанда +атанде +атас +ату +ать +ау +ах +ах вон что +ах вот как +ах вот что +ах так +ах ты +ах-ах +ах-ах-ах +ахти +ахти-хти +ахтунг +ба +ба, +ба, +ба +баиньки +баиньки-баю +бай-бай +банзай +баста +бац +баю +баю-бай +баю-баюшки-баю +баюшки-баю +бе +безобразие +бинго +бис +блин +бо-бо +бр +будет +буц +бяша +вау +вах +виват +вира +вон +всё +всего +вуаля +га +гав +гайда +ге +гей +геть +гип-гип +гип-гип-ура +гля +глядь +гм +го +гой +гоп +гоп-ля +гопля +гугу +гули +гулиньки +гуль +да-а +да-да +да-да-да +долой +досвидос +дык +ё +ё-моё +ей-богу +ей-Богу +ей-ей +ёклмн +ёшкин кот +и +и-го-го +извините +исполать +их +йоу +как бы не так +кап +карамба +кек +кис-кис +ко-ко-ко +кругом +крутяк +кусь +кхм +кш +кыс +кыш +ловко +ля-ля-ля +м +м-да +м-м +м-м-м +майна +мама миа +маран-афа +марш +матушки +мах +ме +мерси +могила +мотор +мяу +н-да +нате +не-ет +ни-ни +но +но-но +ну +ну-ка +ну-кась +ну-кася +ну-ко +ну-кось +ну-ну +ну-тка +нуте +нуте-ка +нуте-с +ня +о +о-го-го +о-ля-ля +о-о +о-хо-хо +о’кей +оба-на +ого +огонь +однако +одначе +ой +ой-йо +ок +окей +оп +опа +опаньки +опля +осторожно +отставить +оу +ох +охоньки +охохоньки +охохонюшки +охохохоньки +охохошеньки +охти +пак +пас +пиль +пли +плие +поди +подожди +покеда +полундра +постой +превед +прекрасно +привет +проехали +прозит +проклятие +простите +прочь +прощайте +прысь +пст +пух +пфу +пфуй +пшш +р +раз-два +салам +салют +салям +светыньки +слышь +сорри +сорян +спокойно +стоп +стопэ +супер +счастливо +тик-так +то-то +топ +точнямба +тпру +трах +тррр +тс +тсс +тубо +тьфу +тю +тюк +тяф +у +увы +угу +ужо +ужотко +упс +ура +уря +уси-пуси +усь +уть +уф +ух +фап +фас +фи +физкульт-привет +фить +фу +фу-ты +фу-фу +фуй +фук +фух +фырк +фьють +фюйть + +ха +ха-ха +ха-ха-ха +хана +хаюшки +хвать +хе +хе-хе +хех +хи +хи-хи +хлесть +хлобысть +хлысть +хлясть +хм +хны +хо-хо +хорэ +хр-р-р +хрусть +хрясть +хрясь +ц-ц-ц +цап +цок +цып-цып +цыц +чао +чёрт его знает +чирик +чих +чу +чур +чух +чш +ых +э-ге-ге +э-хе-хе +эва +В, +к, +до, +по, +через, +после, +в течение, +в продолжение, +в заключение +В, +до, +из-за, +за, +к, +над, +под, +перед, +у, +через, +возле, +мимо, +около и т. д. +Из-за, +от, +по, +ради, +благодаря, +в силу, +ввиду, +вследствие +За, +для, +по, +на, +к, +ради +Вопреки, +несмотря на +О, +об, +обо, +про, +насчёт +С, +вроде, +наподобие, +как +С, +без \ No newline at end of file diff --git a/TagsCloudVisualization/CircularCloudLayouters/CircularCloudLayouter.cs b/TagsCloudVisualization/CircularCloudLayouters/CircularCloudLayouter.cs new file mode 100644 index 00000000..fb13f05d --- /dev/null +++ b/TagsCloudVisualization/CircularCloudLayouters/CircularCloudLayouter.cs @@ -0,0 +1,39 @@ +using System.Drawing; +using TagsCloudVisualization.LayoutAlgorithms; + +namespace TagsCloudVisualization.CircularCloudLayouters; + +public class CircularCloudLayouter : ICircularCloudLayouter +{ + private readonly ILayoutAlgorithm layoutAlgorithm; + private readonly List addedRectangles = []; + + public CircularCloudLayouter(ILayoutAlgorithm layoutAlgorithm) + { + this.layoutAlgorithm = layoutAlgorithm; + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + Rectangle rectangle; + + do + { + var nextPoint = layoutAlgorithm.CalculateNextPoint(); + + var rectangleLocation = nextPoint - rectangleSize / 2; + + rectangle = new Rectangle(rectangleLocation, rectangleSize); + + } while (IntersectWithAddedRectangles(rectangle)); + + addedRectangles.Add(rectangle); + + return rectangle; + } + + private bool IntersectWithAddedRectangles(Rectangle rectangle) + { + return addedRectangles.Any(addedRectangle => addedRectangle.IntersectsWith(rectangle)); + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/CircularCloudLayouters/ICircularCloudLayouter.cs b/TagsCloudVisualization/CircularCloudLayouters/ICircularCloudLayouter.cs new file mode 100644 index 00000000..6d051edd --- /dev/null +++ b/TagsCloudVisualization/CircularCloudLayouters/ICircularCloudLayouter.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloudVisualization.CircularCloudLayouters; + +public interface ICircularCloudLayouter +{ + Rectangle PutNextRectangle(Size rectangleSize); +} \ No newline at end of file diff --git a/TagsCloudVisualization/ColorGenerator/ColorGenerator.cs b/TagsCloudVisualization/ColorGenerator/ColorGenerator.cs new file mode 100644 index 00000000..00819685 --- /dev/null +++ b/TagsCloudVisualization/ColorGenerator/ColorGenerator.cs @@ -0,0 +1,26 @@ +using System.Drawing; +using TagsCloudVisualization.Settings; + +namespace TagsCloudVisualization.ColorGenerator; + +public class ColorGenerator : IColorGenerator +{ + private readonly ColorGeneratorSettings colorGeneratorSettings; + private readonly Random random; + + public ColorGenerator(ColorGeneratorSettings colorGeneratorSettings) + { + this.colorGeneratorSettings = colorGeneratorSettings; + random = new Random(); + } + + public Color GetColor() + { + if (colorGeneratorSettings.ColorName == "random") + { + return Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)); + } + + return Color.FromName(colorGeneratorSettings.ColorName); + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/ColorGenerator/IColorGenerator.cs b/TagsCloudVisualization/ColorGenerator/IColorGenerator.cs new file mode 100644 index 00000000..e9caecde --- /dev/null +++ b/TagsCloudVisualization/ColorGenerator/IColorGenerator.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloudVisualization.ColorGenerator; + +public interface IColorGenerator +{ + Color GetColor(); +} \ No newline at end of file diff --git a/TagsCloudVisualization/CommandLineOptions.cs b/TagsCloudVisualization/CommandLineOptions.cs new file mode 100644 index 00000000..fa590f23 --- /dev/null +++ b/TagsCloudVisualization/CommandLineOptions.cs @@ -0,0 +1,49 @@ +using CommandLine; +using TagsCloudVisualization.Settings; + +namespace TagsCloudVisualization; + +public class CommandLineOptions +{ + [Option("pathToBoringWords", Default = "BoringWords.txt", HelpText = "Path to exclude boring words")] + public string PathToBoringWords { get; set; } + + [Option('t', "pathToText", Default = "Text.txt", HelpText = "Path to text for the words cloud")] + public string PathToText { get; set; } + + [Option('s', "pathToSaveDirectory", Default = "Images", HelpText = "Path to directory to save image")] + public string PathToSaveDirectory { get; set; } + + [Option('n', "fileName", Default = "image", HelpText = "Name of the file to save")] + public string FileName { get; set; } + + [Option("fileFormat", Default = "png", HelpText = "Extension file to save. Example: png")] + public string FileFormat { get; set; } + + [Option("stepIncreasingAngle", Default = CircularLayoutAlgorithmSettings.OneDegree, HelpText = "Delta angle for the spiral.")] + public double StepIncreasingAngle { get; set; } + + [Option("stepIncreasingRadius", Default = 1, HelpText = "Delta radius for the spiral.")] + public double StepIncreasingRadius { get; set; } + + [Option("imageWidth", Default = 1920, HelpText = "Image width")] + public int ImageWidth { get; set; } + + [Option("imageHeight", Default = 1080, HelpText = "Image height")] + public int ImageHeight { get; set; } + + [Option('b', "backgroundColor", Default = "white", HelpText = "Image background color.")] + public string BackgroundColor { get; set; } + + [Option("color", Default = "random", HelpText = "Color of the words. Default is random")] + public string Color { get; set; } + + [Option("font", Default = "Times New Roman", HelpText = "Font of the words")] + public string Font { get; set; } + + [Option("minFontSize", Default = 10, HelpText = "Minimum word font size")] + public int MinFontSize { get; set; } + + [Option("maxFontSize", Default = 50, HelpText = "Maximum word font size")] + public int MaxFontSize { get; set; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Di/DiContainer.cs b/TagsCloudVisualization/Di/DiContainer.cs new file mode 100644 index 00000000..ee8e3c9c --- /dev/null +++ b/TagsCloudVisualization/Di/DiContainer.cs @@ -0,0 +1,75 @@ +using Autofac; +using TagsCloudVisualization.CircularCloudLayouters; +using TagsCloudVisualization.ColorGenerator; +using TagsCloudVisualization.FileReaders; +using TagsCloudVisualization.LayoutAlgorithms; +using TagsCloudVisualization.Settings; +using TagsCloudVisualization.TagLayouters; +using TagsCloudVisualization.TextHandlers; +using TagsCloudVisualization.Visualization; + +namespace TagsCloudVisualization.Di; + +public class DiContainer +{ + public static IContainer Configure(CommandLineOptions options) + { + var builder = new ContainerBuilder(); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("colorName", options.BackgroundColor) + }); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("stepIncreasingAngle", options.StepIncreasingAngle), + new NamedParameter("stepIncreasingRadius", options.StepIncreasingRadius) + }); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("colorName", options.Color) + }); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("filePath", options.PathToSaveDirectory), + new NamedParameter("fileName", options.FileName), + new NamedParameter("fileFormat", options.FileFormat) + }); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("width", options.ImageWidth), + new NamedParameter("height", options.ImageHeight) + }); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("fontName", options.Font), + new NamedParameter("minSize", options.MinFontSize), + new NamedParameter("maxSize", options.MaxFontSize) + }); + + builder.RegisterType().WithParameters(new[] + { + new NamedParameter("pathToBoringWords", options.PathToBoringWords), + new NamedParameter("pathToText", options.PathToText), + }); + + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType(); + + return builder.Build(); + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/FileReaders/DocFileReader.cs b/TagsCloudVisualization/FileReaders/DocFileReader.cs new file mode 100644 index 00000000..4c4d1364 --- /dev/null +++ b/TagsCloudVisualization/FileReaders/DocFileReader.cs @@ -0,0 +1,26 @@ +using Spire.Doc; + +namespace TagsCloudVisualization.FileReaders; + +public class DocFileReader : IFileReader +{ + public bool CanRead(string pathToFile) + { + return pathToFile.Split('.')[^1].Equals("doc", StringComparison.InvariantCultureIgnoreCase); + } + + public List Read(string pathToFile) + { + var doc = new Document(); + + doc.LoadFromFile(pathToFile); + + var text = doc.GetText(); + var paragraphs = text + .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) + .Skip(1); + var wordsInText = WordsHandlerHelper.GetWordsInText(paragraphs); + + return wordsInText; + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/FileReaders/DocxFileReader.cs b/TagsCloudVisualization/FileReaders/DocxFileReader.cs new file mode 100644 index 00000000..fd194d58 --- /dev/null +++ b/TagsCloudVisualization/FileReaders/DocxFileReader.cs @@ -0,0 +1,22 @@ +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; + +namespace TagsCloudVisualization.FileReaders; + +public class DocxFileReader : IFileReader +{ + public bool CanRead(string path) + { + return path.Split('.')[^1].Equals("docx", StringComparison.InvariantCultureIgnoreCase); + } + + public List Read(string path) + { + using var wordDocument = WordprocessingDocument.Open(path, false); + var body = wordDocument.MainDocumentPart?.Document.Body; + var paragraphsOfText = body?.Descendants().Select(text => text.Text); + var wordsInText = WordsHandlerHelper.GetWordsInText(paragraphsOfText!); + + return wordsInText; + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/FileReaders/IFileReader.cs b/TagsCloudVisualization/FileReaders/IFileReader.cs new file mode 100644 index 00000000..b0bb42dc --- /dev/null +++ b/TagsCloudVisualization/FileReaders/IFileReader.cs @@ -0,0 +1,8 @@ +namespace TagsCloudVisualization.FileReaders; + +public interface IFileReader +{ + bool CanRead(string pathToFile); + + List Read(string pathToFile); +} \ No newline at end of file diff --git a/TagsCloudVisualization/FileReaders/TxtFileReader.cs b/TagsCloudVisualization/FileReaders/TxtFileReader.cs new file mode 100644 index 00000000..7acd0a8a --- /dev/null +++ b/TagsCloudVisualization/FileReaders/TxtFileReader.cs @@ -0,0 +1,19 @@ +namespace TagsCloudVisualization.FileReaders; + +public class TxtFileReader : IFileReader +{ + public bool CanRead(string pathToFile) + { + return pathToFile.Split('.')[^1].Equals("txt", StringComparison.InvariantCultureIgnoreCase); + } + + public List Read(string pathToFile) + { + var paragraphs = File.ReadAllText(pathToFile) + .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + + var wordsInText = WordsHandlerHelper.GetWordsInText(paragraphs); + + return wordsInText; + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/FileReaders/WordsHandlerHelper.cs b/TagsCloudVisualization/FileReaders/WordsHandlerHelper.cs new file mode 100644 index 00000000..3c2ee35c --- /dev/null +++ b/TagsCloudVisualization/FileReaders/WordsHandlerHelper.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; + +namespace TagsCloudVisualization.FileReaders; + +public class WordsHandlerHelper +{ + public static List GetWordsInText(IEnumerable paragraphsOfText) + { + var words = new List(); + + foreach (var paragraph in paragraphsOfText) + { + var wordsInParagraph = Regex.Matches(paragraph, @"\b[a-zA-Zа-яА-Я]+\b") + .Select(word => word.Value); + + words.AddRange(wordsInParagraph); + } + + return words; + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Images/image.png b/TagsCloudVisualization/Images/image.png new file mode 100644 index 00000000..d0c7a716 Binary files /dev/null and b/TagsCloudVisualization/Images/image.png differ diff --git a/TagsCloudVisualization/LayoutAlgorithms/CircularLayoutAlgorithm.cs b/TagsCloudVisualization/LayoutAlgorithms/CircularLayoutAlgorithm.cs new file mode 100644 index 00000000..8c44a19c --- /dev/null +++ b/TagsCloudVisualization/LayoutAlgorithms/CircularLayoutAlgorithm.cs @@ -0,0 +1,43 @@ +using System.Drawing; +using TagsCloudVisualization.Settings; + +namespace TagsCloudVisualization.LayoutAlgorithms; + +public class CircularLayoutAlgorithm : ILayoutAlgorithm +{ + private readonly Point center; + private readonly double stepIncreasingAngle; + private readonly int stepIncreasingRadius; + private double currentAngleOfCircle; + private double currentRadiusOfCircle; + private const double FullCircleRotation = 2 * Math.PI; + + public CircularLayoutAlgorithm(CircularLayoutAlgorithmSettings settings) + { + if (settings.StepIncreasingRadius <= 0) + throw new ArgumentException("The parameter 'stepIncreasingRadius' is less than or equal to zero"); + if (settings.StepIncreasingAngle == 0) + throw new ArgumentException("The parameter 'stepIncreasingAngle' is zero"); + + center = new Point(0, 0); + stepIncreasingAngle = settings.StepIncreasingAngle; + stepIncreasingRadius = settings.StepIncreasingRadius; + } + + public Point CalculateNextPoint() + { + var x = center.X + (int)(currentRadiusOfCircle * Math.Cos(currentAngleOfCircle)); + var y = center.Y + (int)(currentRadiusOfCircle * Math.Sin(currentAngleOfCircle)); + + currentAngleOfCircle += stepIncreasingAngle; + + // проверяем не прошли ли целый круг или равен ли текущий радиус нулю + if (currentAngleOfCircle > FullCircleRotation || currentRadiusOfCircle == 0) + { + currentAngleOfCircle = 0; + currentRadiusOfCircle += stepIncreasingRadius; + } + + return new Point(x, y); + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/LayoutAlgorithms/ILayoutAlgorithm.cs b/TagsCloudVisualization/LayoutAlgorithms/ILayoutAlgorithm.cs new file mode 100644 index 00000000..abd078f6 --- /dev/null +++ b/TagsCloudVisualization/LayoutAlgorithms/ILayoutAlgorithm.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloudVisualization.LayoutAlgorithms; + +public interface ILayoutAlgorithm +{ + Point CalculateNextPoint(); +} \ No newline at end of file diff --git a/TagsCloudVisualization/Program.cs b/TagsCloudVisualization/Program.cs new file mode 100644 index 00000000..5fe2e290 --- /dev/null +++ b/TagsCloudVisualization/Program.cs @@ -0,0 +1,24 @@ +using Autofac; +using CommandLine; +using TagsCloudVisualization; +using TagsCloudVisualization.Di; +using TagsCloudVisualization.Visualization; + +Parser.Default.ParseArguments(args) + .WithParsed(commandLineOptions => + { + var container = DiContainer.Configure(commandLineOptions); + var cloudCreator = container.Resolve(); + var image = cloudCreator.CreateImage(); + var imageSaver = container.Resolve(); + + imageSaver.Save(image); + }) + .WithNotParsed(errors => + { + foreach (var error in errors) + { + Console.WriteLine(error.ToString()); + } + Environment.Exit(1); + }); \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/BackgroundSettings.cs b/TagsCloudVisualization/Settings/BackgroundSettings.cs new file mode 100644 index 00000000..03256972 --- /dev/null +++ b/TagsCloudVisualization/Settings/BackgroundSettings.cs @@ -0,0 +1,13 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Settings; + +public class BackgroundSettings +{ + public BackgroundSettings(string colorName) + { + BackgroundColor = Color.FromName(colorName); + } + + public Color BackgroundColor { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/CircularLayoutAlgorithmSettings.cs b/TagsCloudVisualization/Settings/CircularLayoutAlgorithmSettings.cs new file mode 100644 index 00000000..ef2aa6fc --- /dev/null +++ b/TagsCloudVisualization/Settings/CircularLayoutAlgorithmSettings.cs @@ -0,0 +1,16 @@ +namespace TagsCloudVisualization.Settings; + +public class CircularLayoutAlgorithmSettings +{ + public const double OneDegree = Math.PI / 180; + + public CircularLayoutAlgorithmSettings(double stepIncreasingAngle = OneDegree, int stepIncreasingRadius = 1) + { + StepIncreasingAngle = stepIncreasingAngle; + StepIncreasingRadius = stepIncreasingRadius; + } + + public double StepIncreasingAngle { get; } + + public int StepIncreasingRadius { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/ColorGeneratorSettings.cs b/TagsCloudVisualization/Settings/ColorGeneratorSettings.cs new file mode 100644 index 00000000..10046f28 --- /dev/null +++ b/TagsCloudVisualization/Settings/ColorGeneratorSettings.cs @@ -0,0 +1,11 @@ +namespace TagsCloudVisualization.Settings; + +public class ColorGeneratorSettings +{ + public ColorGeneratorSettings(string colorName) + { + ColorName = colorName; + } + + public string ColorName { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/ImageSaverSettings.cs b/TagsCloudVisualization/Settings/ImageSaverSettings.cs new file mode 100644 index 00000000..ed005e51 --- /dev/null +++ b/TagsCloudVisualization/Settings/ImageSaverSettings.cs @@ -0,0 +1,17 @@ +namespace TagsCloudVisualization.Settings; + +public class ImageSaverSettings +{ + public ImageSaverSettings(string filePath, string fileName, string fileFormat) + { + FilePath = filePath; + FileName = fileName; + FileFormat = fileFormat; + } + + public string FilePath { get; } + + public string FileName { get; } + + public string FileFormat { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/ImageSettings.cs b/TagsCloudVisualization/Settings/ImageSettings.cs new file mode 100644 index 00000000..5529b812 --- /dev/null +++ b/TagsCloudVisualization/Settings/ImageSettings.cs @@ -0,0 +1,14 @@ +namespace TagsCloudVisualization.Settings; + +public class ImageSettings +{ + public ImageSettings(int width, int height) + { + Width = width; + Height = height; + } + + public int Width { get; } + + public int Height { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/TagLayouterSettings.cs b/TagsCloudVisualization/Settings/TagLayouterSettings.cs new file mode 100644 index 00000000..79a1d991 --- /dev/null +++ b/TagsCloudVisualization/Settings/TagLayouterSettings.cs @@ -0,0 +1,19 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Settings; + +public class TagLayouterSettings +{ + public TagLayouterSettings(int minSize, int maxSize, string fontName) + { + MinSize = minSize; + FontFamily = new FontFamily(fontName); + MaxSize = maxSize; + } + + public FontFamily FontFamily { get; } + + public int MinSize { get; } + + public int MaxSize { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Settings/TextHandlerSettings.cs b/TagsCloudVisualization/Settings/TextHandlerSettings.cs new file mode 100644 index 00000000..46738086 --- /dev/null +++ b/TagsCloudVisualization/Settings/TextHandlerSettings.cs @@ -0,0 +1,14 @@ +namespace TagsCloudVisualization.Settings; + +public class TextHandlerSettings +{ + public TextHandlerSettings(string pathToBoringWords, string pathToText) + { + PathToBoringWords = pathToBoringWords; + PathToText = pathToText; + } + + public string PathToBoringWords { get; } + + public string PathToText { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/TagCloudCreator.cs b/TagsCloudVisualization/TagCloudCreator.cs new file mode 100644 index 00000000..94e906dc --- /dev/null +++ b/TagsCloudVisualization/TagCloudCreator.cs @@ -0,0 +1,25 @@ +using System.Drawing; +using TagsCloudVisualization.TagLayouters; +using TagsCloudVisualization.Visualization; + +namespace TagsCloudVisualization; + +public class TagCloudCreator +{ + private readonly ITagLayouter tagLayouter; + private readonly IImageDrawer imageDrawer; + + public TagCloudCreator(ITagLayouter tagLayouter, IImageDrawer imageDrawer) + { + this.tagLayouter = tagLayouter; + this.imageDrawer = imageDrawer; + } + + public Bitmap CreateImage() + { + var tags = tagLayouter.GetTags(); + var bitmap = imageDrawer.Draw(tags); + + return bitmap; + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/TagLayouters/ITagLayouter.cs b/TagsCloudVisualization/TagLayouters/ITagLayouter.cs new file mode 100644 index 00000000..32b826c5 --- /dev/null +++ b/TagsCloudVisualization/TagLayouters/ITagLayouter.cs @@ -0,0 +1,8 @@ +using TagsCloudVisualization.Tags; + +namespace TagsCloudVisualization.TagLayouters; + +public interface ITagLayouter +{ + IEnumerable GetTags(); +} \ No newline at end of file diff --git a/TagsCloudVisualization/TagLayouters/TagLayouter.cs b/TagsCloudVisualization/TagLayouters/TagLayouter.cs new file mode 100644 index 00000000..b313353c --- /dev/null +++ b/TagsCloudVisualization/TagLayouters/TagLayouter.cs @@ -0,0 +1,60 @@ +using System.Drawing; +using TagsCloudVisualization.CircularCloudLayouters; +using TagsCloudVisualization.Settings; +using TagsCloudVisualization.Tags; +using TagsCloudVisualization.TextHandlers; + +namespace TagsCloudVisualization.TagLayouters; + +public class TagLayouter : ITagLayouter +{ + private readonly ICircularCloudLayouter circularCloudLayouter; + private readonly ITextHandler textHandler; + private readonly TagLayouterSettings tagLayouterSettings; + private readonly Graphics graphics; + + public TagLayouter( + ICircularCloudLayouter circularCloudLayouter, + ITextHandler textHandler, + TagLayouterSettings tagLayouterSettings) + { + this.textHandler = textHandler; + this.tagLayouterSettings = tagLayouterSettings; + this.circularCloudLayouter = circularCloudLayouter; + graphics = Graphics.FromHwnd(IntPtr.Zero); + } + + public IEnumerable GetTags() + { + var wordsCount = textHandler.GetWordsCount(); + var maxWordCount = wordsCount.First().Value; + var minWordCount = wordsCount.Last().Value; + + foreach (var wordWithCount in wordsCount) + { + var fontSize = GetFontSize(minWordCount, maxWordCount, wordWithCount.Value); + yield return new Tag(wordWithCount.Key, + fontSize, + circularCloudLayouter.PutNextRectangle(GetWordSize(wordWithCount.Key, fontSize)), + tagLayouterSettings.FontFamily); + } + } + + private int GetFontSize(int minWordCount, int maxWordCount, int wordCount) + { + if (maxWordCount > minWordCount) + { + return tagLayouterSettings.MinSize + (tagLayouterSettings.MaxSize - tagLayouterSettings.MinSize) + * (wordCount - minWordCount) / (maxWordCount - minWordCount); + } + + return tagLayouterSettings.MaxSize; + } + + private Size GetWordSize(string content, int fontSize) + { + var sizeF = graphics.MeasureString(content, new Font(tagLayouterSettings.FontFamily, fontSize)); + + return new Size((int)Math.Ceiling(sizeF.Width), (int)Math.Ceiling(sizeF.Height)); + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Tags/Tag.cs b/TagsCloudVisualization/Tags/Tag.cs new file mode 100644 index 00000000..057912e0 --- /dev/null +++ b/TagsCloudVisualization/Tags/Tag.cs @@ -0,0 +1,22 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Tags; + +public class Tag +{ + public Tag(string content, int size, Rectangle rectangle, FontFamily font) + { + Content = content; + Size = size; + Rectangle = rectangle; + Font = font; + } + + public string Content { get; } + + public int Size { get; } + + public Rectangle Rectangle { get; } + + public FontFamily Font { get; } +} \ No newline at end of file diff --git a/TagsCloudVisualization/TagsCloudVisualization.csproj b/TagsCloudVisualization/TagsCloudVisualization.csproj new file mode 100644 index 00000000..d7603ee8 --- /dev/null +++ b/TagsCloudVisualization/TagsCloudVisualization.csproj @@ -0,0 +1,31 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + + + + Always + + + + Always + + + + diff --git a/TagsCloudVisualization/Text.txt b/TagsCloudVisualization/Text.txt new file mode 100644 index 00000000..d197cd74 --- /dev/null +++ b/TagsCloudVisualization/Text.txt @@ -0,0 +1,196 @@ +программирование +технологии +искусственный +интеллект +обучение +алгоритмы +разработка +инновации +анализ +машинное +обучение +кодирование +дизайн +проектирование +данные +информация +интернет +творчество +решения +архитектура +оптимизация +исследования +наука +образование +литература +музыка +искусство +математика +физика +химия +биология +философия +история +география +экономика +бизнес +стартап +продажи +маркетинг +финансы +менеджмент +руководство +лидерство +команда +коммуникация +переговоры +успех +мотивация +достижение +креативность +идея +проект +стратегия +планирование +организация +инструменты +ресурсы +инфраструктура +эффективность +результат +производительность +системы +процессы +технологии +инновации +автоматизация +цифровизация +моделирование +исследования +эксперименты +обработка +данных +аналитика +статистика +предсказания +модели +обучение +развитие +совершенствование +успех +цель +достижение +работа +карьера +профессионализм +компетенция +умение +навык +опыт +знание +изучение +открытие +понимание +мышление +логика +рассуждение +аргумент +заключение +гипотеза +проверка +выводы +проблема +решение +задача +цель +информация +данные +документы +тексты +язык +общение +разговор +диалог +дискуссия +аргументы +мнение +обсуждение +переговоры +согласие +конфликт +решение +компромисс +сотрудничество +взаимопомощь +поддержка +доверие +отношения +команда +группа +коллектив +общество +государство +культура +традиции +обычаи +ценности +мораль +нравственность +этика +политика +экономика +экология +технологии +инновации +будущее +наука +космос +исследования +эксперименты +техника +инженерия +механика +физика +математика +астрономия +биотехнологии +нанотехнологии +электроника +робототехника +искусственный +интеллект +облачные +технологии +блокчейн +кибербезопасность +компьютеры +программное +обеспечение +алгоритмы +кодирование +сети +интернет +социальные +сети +коммуникации +медиа +журналистика +информация +новости +документы +тексты +литература +поэзия +романы +повести +рассказы +драматургия +театр +музыка +живопись +кино +фотография +мода +дизайн +архитектура +скульптура \ No newline at end of file diff --git a/TagsCloudVisualization/TextHandlers/ITextHandler.cs b/TagsCloudVisualization/TextHandlers/ITextHandler.cs new file mode 100644 index 00000000..5c4c5450 --- /dev/null +++ b/TagsCloudVisualization/TextHandlers/ITextHandler.cs @@ -0,0 +1,10 @@ +namespace TagsCloudVisualization.TextHandlers; + +public interface ITextHandler +{ + /// + /// Метод-обработчик исходного текста + /// + /// Словарь: слово - количество + Dictionary GetWordsCount(); +} \ No newline at end of file diff --git a/TagsCloudVisualization/TextHandlers/TextHandler.cs b/TagsCloudVisualization/TextHandlers/TextHandler.cs new file mode 100644 index 00000000..d34904c9 --- /dev/null +++ b/TagsCloudVisualization/TextHandlers/TextHandler.cs @@ -0,0 +1,53 @@ +using TagsCloudVisualization.FileReaders; +using TagsCloudVisualization.Settings; + +namespace TagsCloudVisualization.TextHandlers; + +public class TextHandler : ITextHandler +{ + private readonly IFileReader[] readers; + private readonly TextHandlerSettings settings; + + public TextHandler(IFileReader[] readers, TextHandlerSettings settings) + { + this.readers = readers; + this.settings = settings; + } + + public Dictionary GetWordsCount() + { + var reader = GetReader(settings.PathToText); + var words = reader.Read(settings.PathToText); + var boringWordsReader = GetReader(settings.PathToBoringWords); + var boringWords = boringWordsReader.Read(settings.PathToBoringWords).ToHashSet(); + var wordsCount = new Dictionary(); + + foreach (var lowerWord in words.Select(word => word.ToLower())) + { + if (wordsCount.TryGetValue(lowerWord, out var value)) + { + wordsCount[lowerWord] = ++value; + continue; + } + if (!boringWords.Contains(lowerWord)) + { + wordsCount[lowerWord] = 1; + } + } + + return words + .Select(word => word.ToLower()) + .Where(word => !boringWords.Contains(word)) + .GroupBy(word => word) + .ToDictionary(group => group.Key, group => group.Count()) + .OrderByDescending(pair => pair.Value) + .ToDictionary(pair => pair.Key, pair => pair.Value); + } + + private IFileReader GetReader(string path) + { + var reader = readers.FirstOrDefault(r => r.CanRead(path)); + + return reader ?? throw new ArgumentException($"Can't read file with path {path}"); + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Visualization/IImageDrawer.cs b/TagsCloudVisualization/Visualization/IImageDrawer.cs new file mode 100644 index 00000000..e8a7898f --- /dev/null +++ b/TagsCloudVisualization/Visualization/IImageDrawer.cs @@ -0,0 +1,9 @@ +using System.Drawing; +using TagsCloudVisualization.Tags; + +namespace TagsCloudVisualization.Visualization; + +public interface IImageDrawer +{ + Bitmap Draw(IEnumerable tags); +} \ No newline at end of file diff --git a/TagsCloudVisualization/Visualization/IImageSaver.cs b/TagsCloudVisualization/Visualization/IImageSaver.cs new file mode 100644 index 00000000..cea2dd1b --- /dev/null +++ b/TagsCloudVisualization/Visualization/IImageSaver.cs @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace TagsCloudVisualization.Visualization; + +public interface IImageSaver +{ + void Save(Bitmap bitmap); +} \ No newline at end of file diff --git a/TagsCloudVisualization/Visualization/ImageDrawer.cs b/TagsCloudVisualization/Visualization/ImageDrawer.cs new file mode 100644 index 00000000..7005ab20 --- /dev/null +++ b/TagsCloudVisualization/Visualization/ImageDrawer.cs @@ -0,0 +1,47 @@ +using System.Drawing; +using TagsCloudVisualization.ColorGenerator; +using TagsCloudVisualization.Settings; +using TagsCloudVisualization.Tags; + +namespace TagsCloudVisualization.Visualization; + +public class ImageDrawer : IImageDrawer +{ + private readonly ImageSettings imageSettings; + private readonly BackgroundSettings backgroundSettings; + private readonly IColorGenerator colorGenerator; + + public ImageDrawer( + ImageSettings imageSettings, + BackgroundSettings backgroundSettings, + IColorGenerator colorGenerator) + { + this.imageSettings = imageSettings; + this.backgroundSettings = backgroundSettings; + this.colorGenerator = colorGenerator; + } + + public Bitmap Draw(IEnumerable tags) + { + var bitmap = new Bitmap(imageSettings.Width, imageSettings.Height); + var graphics = Graphics.FromImage(bitmap); + graphics.Clear(backgroundSettings.BackgroundColor); + + foreach (var tag in tags) + { + var font = new Font(tag.Font, tag.Size); + var color = new SolidBrush(colorGenerator.GetColor()); + var rectangle = tag.Rectangle with + { + X = tag.Rectangle.X + imageSettings.Width / 2, + Y = tag.Rectangle.Y + imageSettings.Height / 2 + }; + + graphics.DrawString(tag.Content, + font, + color, + rectangle); + } + return bitmap; + } +} \ No newline at end of file diff --git a/TagsCloudVisualization/Visualization/ImageSaver.cs b/TagsCloudVisualization/Visualization/ImageSaver.cs new file mode 100644 index 00000000..e2726fe9 --- /dev/null +++ b/TagsCloudVisualization/Visualization/ImageSaver.cs @@ -0,0 +1,47 @@ +using System.Drawing; +using System.Drawing.Imaging; +using TagsCloudVisualization.Settings; + +namespace TagsCloudVisualization.Visualization; + +public class ImageSaver : IImageSaver +{ + private readonly ImageSaverSettings settings; + + public ImageSaver(ImageSaverSettings settings) + { + this.settings = settings; + } + + public void Save(Bitmap bitmap) + { + var filePath = settings.FilePath; + var fileName = settings.FileName; + var imageFormat = GetImageFormat(settings.FileFormat); + + if (!Directory.Exists(settings.FilePath)) + { + Directory.CreateDirectory(settings.FilePath); + } + + bitmap.Save(Path.Combine(filePath, $"{fileName}.{settings.FileFormat}"), imageFormat); + } + + private static ImageFormat GetImageFormat(string format) + { + try + { + var imageFormatConverter = new ImageFormatConverter(); + var imageFormat = imageFormatConverter.ConvertFromString(format); + + if (imageFormat != null) + return (ImageFormat)imageFormat; + + throw new ArgumentException($"Can't convert format from this string {format}"); + } + catch (NotSupportedException) + { + throw new NotSupportedException($"File with format {format} doesn't supported"); + } + } +} \ No newline at end of file diff --git a/TagsCloudVisualizationTests/CircularCloudLayoutTests.cs b/TagsCloudVisualizationTests/CircularCloudLayoutTests.cs new file mode 100644 index 00000000..cb41c9e5 --- /dev/null +++ b/TagsCloudVisualizationTests/CircularCloudLayoutTests.cs @@ -0,0 +1,93 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using TagsCloudVisualization.CircularCloudLayouters; +using TagsCloudVisualization.LayoutAlgorithms; +using TagsCloudVisualization.Settings; +using TagsCloudVisualization.Visualization; +using TagsCloudVisualizationTests.Utils; + +namespace TagsCloudVisualizationTests; + +[TestFixture] +public class CircularCloudLayoutTests +{ + private ICircularCloudLayouter cloudLayouter; + private List addedRectangles; + + [SetUp] + public void Setup() + { + cloudLayouter = + new CircularCloudLayouter(new CircularLayoutAlgorithm(new CircularLayoutAlgorithmSettings())); + addedRectangles = []; + } + + [TearDown] + public void TearDown() + { + if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed) + return; + + var pathImageStored = TestContext.CurrentContext.TestDirectory + @"\imageFailedTests"; + + if (!Directory.Exists(pathImageStored)) + { + Directory.CreateDirectory(pathImageStored); + } + + var testName = TestContext.CurrentContext.Test.Name; + + var bitmap = ImageDrawerUtils.DrawLayout(addedRectangles, 10); + + var imageSaver = new ImageSaver(new ImageSaverSettings(pathImageStored, testName, "png")); + + imageSaver.Save(bitmap); + + Console.WriteLine($@"Tag cloud visualization saved to file {pathImageStored}\{testName}.png"); + } + + [TestCase(10, 5, 15)] + [TestCase(50, 30, 100)] + [TestCase(100, 5, 50)] + public void PutNextRectangle_ShouldAddedRectanglesDoNotIntersect(int countRectangles, int minSideLength, + int maxSideLength) + { + var rectangleSizes = GeometryUtils.GenerateRectangleSizes(countRectangles, minSideLength, maxSideLength); + + addedRectangles.AddRange(rectangleSizes.Select(t => cloudLayouter.PutNextRectangle(t))); + + for (var i = 0; i < addedRectangles.Count-1; i++) + { + addedRectangles + .Skip(i + 1) + .Any(addedRectangle => addedRectangle.IntersectsWith(addedRectangles[i])) + .Should() + .BeFalse(); + } + } + + [TestCase(10, 5, 15)] + [TestCase(50, 30, 100)] + [TestCase(100, 5, 50)] + public void CircleShape_ShouldBeCloseToCircle_WhenAddMultipleRectangles(int countRectangles, int minSideLength, + int maxSideLength) + { + var rectangleSizes = GeometryUtils.GenerateRectangleSizes(countRectangles, minSideLength, maxSideLength); + + addedRectangles.AddRange(rectangleSizes.Select(t => cloudLayouter.PutNextRectangle(t))); + + var distances = addedRectangles + .Select(rectangle => + GeometryUtils.CalculateDistanceBetweenCenterRectangleAndCenterCloud(rectangle, new Point(0, 0))) + .ToArray(); + + for (var i = 1; i < distances.Length; i++) + { + var distanceBetweenRectangles = + GeometryUtils.CalculateDistanceBetweenRectangles(addedRectangles[i], addedRectangles[i - 1]); + distances[i].Should().BeApproximately(distances[i - 1], distanceBetweenRectangles); + } + } +} \ No newline at end of file diff --git a/TagsCloudVisualizationTests/CircularLayoutAlgorithmTests.cs b/TagsCloudVisualizationTests/CircularLayoutAlgorithmTests.cs new file mode 100644 index 00000000..5a033adc --- /dev/null +++ b/TagsCloudVisualizationTests/CircularLayoutAlgorithmTests.cs @@ -0,0 +1,82 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using TagsCloudVisualization.LayoutAlgorithms; +using TagsCloudVisualization.Settings; +using TagsCloudVisualizationTests.Utils; + +namespace TagsCloudVisualizationTests; + +[TestFixture] +public class CircularLayoutAlgorithmTests +{ + [TestCase(0)] + [TestCase(-5)] + public void Constructor_ShouldArgumentException_WhenStepIncreasingRadiusHasInvalidValue(int stepIncreasingRadius) + { + var settings = new CircularLayoutAlgorithmSettings(stepIncreasingRadius: stepIncreasingRadius); + + var action = () => + { + _ = new CircularLayoutAlgorithm(settings); + }; + + action + .Should() + .Throw() + .WithMessage("The parameter 'stepIncreasingRadius' is less than or equal to zero"); + } + + [Test] + public void Constructor_ShouldArgumentException_WhenStepIncreasingAngleIsZero() + { + var settings = new CircularLayoutAlgorithmSettings(stepIncreasingAngle: 0); + + var action = () => + { + _ = new CircularLayoutAlgorithm(settings); + }; + + action + .Should() + .Throw() + .WithMessage("The parameter 'stepIncreasingAngle' is zero"); + } + + [TestCase(2)] + [TestCase(7)] + public void CalculateNextPoint_ShouldIncreaseRadius_WhenCalculateTwoPoints(int stepIncreasingRadius) + { + var settings = new CircularLayoutAlgorithmSettings(stepIncreasingRadius: stepIncreasingRadius); + var circularLayoutAlgorithm = + new CircularLayoutAlgorithm(settings); + + var firstPoint = circularLayoutAlgorithm.CalculateNextPoint(); + var secondPoint = circularLayoutAlgorithm.CalculateNextPoint(); + var distanceBetweenPoints = GeometryUtils.CalculateDistanceBetweenPoints(firstPoint, secondPoint); + + distanceBetweenPoints.Should().Be(stepIncreasingRadius); + } + + [TestCase(Math.PI / 4)] + [TestCase(Math.PI / 2)] + public void CalculateNextPoint_ShouldIncreaseAngle_WhenCalculateThreePoints(double stepIncreasingAngle) + { + var stepIncreasingRadius = 2; + var center = new Point(0, 0); + var circularLayoutAlgorithm = new CircularLayoutAlgorithm( + new CircularLayoutAlgorithmSettings(stepIncreasingAngle, stepIncreasingRadius)); + + _ = circularLayoutAlgorithm.CalculateNextPoint(); + var secondPoint = circularLayoutAlgorithm.CalculateNextPoint(); + var thirdPoint = circularLayoutAlgorithm.CalculateNextPoint(); + var distanceBetweenCenterAndSecondPoint = GeometryUtils.CalculateDistanceBetweenPoints(center, secondPoint); + var distanceBetweenCenterAndThirdPoint = GeometryUtils.CalculateDistanceBetweenPoints(center, thirdPoint); + + // проверяем, что точки не равны + secondPoint.Should().NotBe(thirdPoint); + // проверяем, что точки в пределах круга с одним радиусом + distanceBetweenCenterAndSecondPoint.Should().BeLessThanOrEqualTo(stepIncreasingRadius); + distanceBetweenCenterAndThirdPoint.Should().BeLessThanOrEqualTo(stepIncreasingRadius); + } +} \ No newline at end of file diff --git a/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileDoc.doc b/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileDoc.doc new file mode 100644 index 00000000..b52ae0e5 Binary files /dev/null and b/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileDoc.doc differ diff --git a/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileDocx.docx b/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileDocx.docx new file mode 100644 index 00000000..0f25b0d6 Binary files /dev/null and b/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileDocx.docx differ diff --git a/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileTxt.txt b/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileTxt.txt new file mode 100644 index 00000000..48fe91fc --- /dev/null +++ b/TagsCloudVisualizationTests/FileReaderTests/FilesForTests/fileTxt.txt @@ -0,0 +1,3 @@ +Привет, мир +Школа +Промышленной Разработки \ No newline at end of file diff --git a/TagsCloudVisualizationTests/FileReaderTests/TxtFileReaderTests.cs b/TagsCloudVisualizationTests/FileReaderTests/TxtFileReaderTests.cs new file mode 100644 index 00000000..c5c8ce8d --- /dev/null +++ b/TagsCloudVisualizationTests/FileReaderTests/TxtFileReaderTests.cs @@ -0,0 +1,39 @@ +using FluentAssertions; +using NUnit.Framework; +using TagsCloudVisualization.FileReaders; + +namespace TagsCloudVisualizationTests.FileReaderTests; + +[TestFixture] +public class TxtFileReaderTests +{ + [Test] + public void ReadText_TxtFile_ReturnWordsInFile() + { + var reader = new TxtFileReader(); + + var words = reader.Read("FileReaderTests/FilesForTests/fileTxt.txt"); + + words.Should().BeEquivalentTo(["Привет", "мир", "Школа", "Промышленной", "Разработки"]); + } + + [Test] + public void ReadText_DocFile_ReturnWordsInFile() + { + var reader = new DocFileReader(); + + var words = reader.Read("FileReaderTests/FilesForTests/fileDoc.doc"); + + words.Should().BeEquivalentTo(["Привет", "Интересная", "задача", "Любопытно"]); + } + + [Test] + public void ReadText_DocxFile_ReturnWordsInFile() + { + var reader = new DocxFileReader(); + + var words = reader.Read("FileReaderTests/FilesForTests/fileDocx.docx"); + + words.Should().BeEquivalentTo(["Третий", "файл", "Тоже", "работает"]); + } +} \ No newline at end of file diff --git a/TagsCloudVisualizationTests/TagsCloudVisualizationTests.csproj b/TagsCloudVisualizationTests/TagsCloudVisualizationTests.csproj new file mode 100644 index 00000000..f3268415 --- /dev/null +++ b/TagsCloudVisualizationTests/TagsCloudVisualizationTests.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + Always + + + + Always + + + + Always + + + + diff --git a/TagsCloudVisualizationTests/Utils/GeometryUtils.cs b/TagsCloudVisualizationTests/Utils/GeometryUtils.cs new file mode 100644 index 00000000..b6fc5f08 --- /dev/null +++ b/TagsCloudVisualizationTests/Utils/GeometryUtils.cs @@ -0,0 +1,40 @@ +using System.Drawing; + +namespace TagsCloudVisualizationTests.Utils; + +public static class GeometryUtils +{ + public static double CalculateDistanceBetweenCenterRectangleAndCenterCloud(Rectangle rectangle, Point center) + { + var centerRectangle = new Point(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2); + + return CalculateDistanceBetweenPoints(centerRectangle, center); + } + + public static double CalculateDistanceBetweenRectangles(Rectangle rectangle1, Rectangle rectangle2) + { + var centerRectangle1 = new Point(rectangle1.X + rectangle1.Width / 2, rectangle1.Y + rectangle1.Height / 2); + var centerRectangle2 = new Point(rectangle2.X + rectangle2.Width / 2, rectangle2.Y + rectangle2.Height / 2); + + return CalculateDistanceBetweenPoints(centerRectangle1, centerRectangle2); + } + + public static double CalculateDistanceBetweenPoints(Point point1, Point point2) + { + return Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2)); + } + + public static List GenerateRectangleSizes(int countRectangles, int minSideLength, int maxSideLength) + { + var random = new Random(); + + var generatedSizes = Enumerable.Range(0, countRectangles) + .Select(_ => new Size( + random.Next(minSideLength, maxSideLength), + random.Next(minSideLength, maxSideLength)) + ) + .ToList(); + + return generatedSizes; + } +} \ No newline at end of file diff --git a/TagsCloudVisualizationTests/Utils/ImageDrawerUtils.cs b/TagsCloudVisualizationTests/Utils/ImageDrawerUtils.cs new file mode 100644 index 00000000..bcadfdbd --- /dev/null +++ b/TagsCloudVisualizationTests/Utils/ImageDrawerUtils.cs @@ -0,0 +1,40 @@ +using System.Drawing; + +namespace TagsCloudVisualizationTests.Utils; + +public static class ImageDrawerUtils +{ + public static Bitmap DrawLayout(List rectangles, int paddingFromBorders) + { + var minX = rectangles.Min(rectangle => rectangle.Left); + var minY = rectangles.Min(rectangle => rectangle.Top); + var maxX = rectangles.Max(rectangle => rectangle.Right); + var maxY = rectangles.Max(rectangle => rectangle.Bottom); + var width = maxX - minX + paddingFromBorders; + var height = maxY - minY + paddingFromBorders; + + var random = new Random(); + + var bitmap = new Bitmap(width, height); + using var graphics = Graphics.FromImage(bitmap); + + graphics.Clear(Color.White); + + foreach (var rectangle in rectangles) + { + var shiftedRectangle = rectangle with + { + X = rectangle.X - minX + paddingFromBorders, + Y = rectangle.Y - minY + paddingFromBorders + }; + + var randomColor = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); + + var brush = new SolidBrush(randomColor); + graphics.FillRectangle(brush, shiftedRectangle); + graphics.DrawRectangle(Pens.Black, shiftedRectangle); + } + + return bitmap; + } +} \ No newline at end of file diff --git a/di.sln b/di.sln index a50991da..ad817c9f 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}") = "di", "FractalPainter/di.csproj", "{4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudVisualization", "TagsCloudVisualization\TagsCloudVisualization.csproj", "{ED3DFCFA-DD0C-49F8-8419-2CAF794249FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudVisualizationTests", "TagsCloudVisualizationTests\TagsCloudVisualizationTests.csproj", "{98AACAAC-5AB0-4EF5-B4A7-15BF85DB6250}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +16,13 @@ Global {4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU {4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU {4B70F6B3-5C20-40D2-BFC9-D95C18D65DD6}.Release|Any CPU.Build.0 = Release|Any CPU + {ED3DFCFA-DD0C-49F8-8419-2CAF794249FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED3DFCFA-DD0C-49F8-8419-2CAF794249FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED3DFCFA-DD0C-49F8-8419-2CAF794249FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED3DFCFA-DD0C-49F8-8419-2CAF794249FB}.Release|Any CPU.Build.0 = Release|Any CPU + {98AACAAC-5AB0-4EF5-B4A7-15BF85DB6250}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98AACAAC-5AB0-4EF5-B4A7-15BF85DB6250}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98AACAAC-5AB0-4EF5-B4A7-15BF85DB6250}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98AACAAC-5AB0-4EF5-B4A7-15BF85DB6250}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal