diff --git a/TagCloudDi/Applications/ConsoleApplication.cs b/TagCloudDi/Applications/ConsoleApplication.cs new file mode 100644 index 000000000..91a5641fc --- /dev/null +++ b/TagCloudDi/Applications/ConsoleApplication.cs @@ -0,0 +1,14 @@ +using TagCloudDi.Drawer; + +namespace TagCloudDi.Applications +{ + public class ConsoleApplication(IDrawer drawer, Settings settings) : IApplication + { + public void Run() + { + var image = drawer.GetImage(); + image.Save($"{settings.SavePath}.{settings.ImageFormat.ToLower()}", settings.GetFormat()); + Console.WriteLine($"Saved to {settings.SavePath + '.' + settings.ImageFormat.ToLower()}"); + } + } +} diff --git a/TagCloudDi/Applications/IApplication.cs b/TagCloudDi/Applications/IApplication.cs new file mode 100644 index 000000000..617aaace4 --- /dev/null +++ b/TagCloudDi/Applications/IApplication.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.Applications +{ + public interface IApplication + { + void Run(); + } +} diff --git a/TagCloudDi/Container.cs b/TagCloudDi/Container.cs new file mode 100644 index 000000000..228df5f48 --- /dev/null +++ b/TagCloudDi/Container.cs @@ -0,0 +1,33 @@ +using System.Drawing; +using Autofac; +using TagCloudDi.Applications; +using TagCloudDi.Drawer; +using TagCloudDi.Layouter; +using TagCloudDi.TextProcessing; +using IContainer = Autofac.IContainer; + +namespace TagCloudDi +{ + public static class Container + { + public static IContainer SetupContainer(Settings settings) + { + var builder = new ContainerBuilder(); + builder.Register(c => settings); + builder.Register(c => + new ArchimedeanSpiral(new Point( + c.Resolve().ImageWidth / 2, + c.Resolve().ImageHeight / 2), + c.Resolve())) + .As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + return builder.Build(); + } + } +} diff --git a/TagCloudDi/Drawer/Drawer.cs b/TagCloudDi/Drawer/Drawer.cs new file mode 100644 index 000000000..d178c4553 --- /dev/null +++ b/TagCloudDi/Drawer/Drawer.cs @@ -0,0 +1,21 @@ +using System.Drawing; +using TagCloudDi.Layouter; + +namespace TagCloudDi.Drawer +{ + public class Drawer(Settings settings, IRectanglesGenerator rectanglesGenerator) : IDrawer + { + public Image GetImage() + { + var image = new Bitmap(settings.ImageWidth, settings.ImageHeight); + using var gr = Graphics.FromImage(image); + gr.Clear(Color.FromName(settings.BackColor)); + var brush = new SolidBrush(Color.FromName(settings.TextColor)); + foreach (var rectangleData in rectanglesGenerator.GetRectanglesData()) + using (var font = new Font(settings.FontName, rectangleData.fontSize, FontStyle.Regular)) + gr.DrawString(rectangleData.word, font, brush, rectangleData.rectangle); + + return image; + } + } +} diff --git a/TagCloudDi/Drawer/IDrawer.cs b/TagCloudDi/Drawer/IDrawer.cs new file mode 100644 index 000000000..b65f11b41 --- /dev/null +++ b/TagCloudDi/Drawer/IDrawer.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagCloudDi.Drawer +{ + public interface IDrawer + { + public Image GetImage(); + } +} diff --git a/TagCloudDi/Examples/1.png b/TagCloudDi/Examples/1.png new file mode 100644 index 000000000..0a361e0ec Binary files /dev/null and b/TagCloudDi/Examples/1.png differ diff --git a/TagCloudDi/Examples/Custom_Image_Size.png b/TagCloudDi/Examples/Custom_Image_Size.png new file mode 100644 index 000000000..bac453065 Binary files /dev/null and b/TagCloudDi/Examples/Custom_Image_Size.png differ diff --git a/TagCloudDi/Examples/DifferentColors.png b/TagCloudDi/Examples/DifferentColors.png new file mode 100644 index 000000000..785f370b8 Binary files /dev/null and b/TagCloudDi/Examples/DifferentColors.png differ diff --git a/TagCloudDi/Examples/DifferentFormat.jpeg b/TagCloudDi/Examples/DifferentFormat.jpeg new file mode 100644 index 000000000..f93c3e9be Binary files /dev/null and b/TagCloudDi/Examples/DifferentFormat.jpeg differ diff --git a/TagCloudDi/Examples/Different_Font.png b/TagCloudDi/Examples/Different_Font.png new file mode 100644 index 000000000..0e2be6315 Binary files /dev/null and b/TagCloudDi/Examples/Different_Font.png differ diff --git a/TagCloudDi/Examples/Reversed_Spiral_Scale.png b/TagCloudDi/Examples/Reversed_Spiral_Scale.png new file mode 100644 index 000000000..11f2a0b82 Binary files /dev/null and b/TagCloudDi/Examples/Reversed_Spiral_Scale.png differ diff --git a/TagCloudDi/Layouter/ArchimedeanSpiral.cs b/TagCloudDi/Layouter/ArchimedeanSpiral.cs new file mode 100644 index 000000000..4588641d8 --- /dev/null +++ b/TagCloudDi/Layouter/ArchimedeanSpiral.cs @@ -0,0 +1,21 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public class ArchimedeanSpiral(Point centerPoint, Settings settings) : IPointGenerator + { + private double angle { get; set; } + private const double DeltaAngle = Math.PI / 180; + private readonly int scale = settings.SpiralScale; + public Point CenterPoint => centerPoint; + + public Point GetNextPoint() + { + var newX = (int)(centerPoint.X + scale * angle * Math.Cos(angle)); + var newY = (int)(centerPoint.Y + scale * angle * Math.Sin(angle)); + angle += DeltaAngle; + + return new Point(newX, newY); + } + } +} diff --git a/TagCloudDi/Layouter/CircularCloudLayouter.cs b/TagCloudDi/Layouter/CircularCloudLayouter.cs new file mode 100644 index 000000000..9de36d861 --- /dev/null +++ b/TagCloudDi/Layouter/CircularCloudLayouter.cs @@ -0,0 +1,81 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public class CircularCloudLayouter : ILayouter + { + private readonly IPointGenerator pointGenerator; + public readonly List Rectangles = []; + + public CircularCloudLayouter(IPointGenerator pointGenerator) + { + this.pointGenerator = pointGenerator; + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) + throw new ArgumentException( + $"rectangleSize with zero or negative height or width is prohibited!", + nameof(rectangleSize) + ); + while (true) + { + var nextPoint = pointGenerator.GetNextPoint(); + var newPoint = new Point(nextPoint.X - rectangleSize.Width / 2, nextPoint.Y - rectangleSize.Height / 2); + var rectangle = new Rectangle(newPoint, rectangleSize); + if (IsIntersectsWithOthers(rectangle)) continue; + rectangle = GetCloserToCenterRectangle(rectangle); + Rectangles.Add(rectangle); + break; + } + + return Rectangles[^1]; + } + + private bool IsIntersectsWithOthers(Rectangle rectangle) => + Rectangles.Any(x => x.IntersectsWith(rectangle)); + + private Rectangle GetCloserToCenterRectangle(Rectangle rectangle) + { + var directions = GetDirection(rectangle); + foreach (var direction in directions) + { + var newRectangle = GetMovedRectangle(rectangle, direction.X, direction.Y); + while (!IsIntersectsWithOthers(newRectangle)) + { + if (pointGenerator.CenterPoint.X - newRectangle.Size.Width / 2 == newRectangle.X + || pointGenerator.CenterPoint.Y - newRectangle.Size.Height / 2 == newRectangle.Y) + break; + rectangle = newRectangle; + newRectangle = GetMovedRectangle(rectangle, direction.X, direction.Y); + } + } + + return rectangle; + } + + private List<(int X, int Y)> GetDirection(Rectangle rectangle) + { + var horizontalDiffer = pointGenerator.CenterPoint.X - rectangle.Size.Width / 2 - rectangle.X; + var verticalDiffer = pointGenerator.CenterPoint.Y - rectangle.Size.Height / 2 - rectangle.Y; + var directions = new List<(int X, int Y)>(); + if (horizontalDiffer != 0 && verticalDiffer != 0) + directions.Add((horizontalDiffer > 0 ? 1 : -1, verticalDiffer > 0 ? 1 : -1)); + if (horizontalDiffer != 0) + directions.Add((horizontalDiffer > 0 ? 1 : -1, 0)); + if (verticalDiffer != 0) + directions.Add((0, verticalDiffer > 0 ? 1 : -1)); + return directions; + } + + private static Rectangle GetMovedRectangle(Rectangle rectangle, int xDelta, int yDelta) => + new( + new Point( + rectangle.X + xDelta, + rectangle.Y + yDelta + ), + rectangle.Size + ); + } +} diff --git a/TagCloudDi/Layouter/ILayouter.cs b/TagCloudDi/Layouter/ILayouter.cs new file mode 100644 index 000000000..0f6dc92e1 --- /dev/null +++ b/TagCloudDi/Layouter/ILayouter.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public interface ILayouter + { + public Rectangle PutNextRectangle(Size rectangleSize); + } +} diff --git a/TagCloudDi/Layouter/IPointGenerator.cs b/TagCloudDi/Layouter/IPointGenerator.cs new file mode 100644 index 000000000..01e76030e --- /dev/null +++ b/TagCloudDi/Layouter/IPointGenerator.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public interface IPointGenerator + { + public Point GetNextPoint(); + public Point CenterPoint { get; } + } +} diff --git a/TagCloudDi/Layouter/IRectanglesGenerator.cs b/TagCloudDi/Layouter/IRectanglesGenerator.cs new file mode 100644 index 000000000..9ec7d0a7b --- /dev/null +++ b/TagCloudDi/Layouter/IRectanglesGenerator.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.Layouter +{ + public interface IRectanglesGenerator + { + public IEnumerable GetRectanglesData(); + } +} diff --git a/TagCloudDi/Layouter/RectangleData.cs b/TagCloudDi/Layouter/RectangleData.cs new file mode 100644 index 000000000..bb2017b70 --- /dev/null +++ b/TagCloudDi/Layouter/RectangleData.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public class RectangleData + { + public readonly Rectangle rectangle; + public readonly string word; + public readonly float fontSize; + + public RectangleData(Rectangle rectangle, string word, float fontSize) + { + this.fontSize = fontSize; + this.word = word; + this.rectangle = rectangle; + } + } +} diff --git a/TagCloudDi/Layouter/RectanglesGenerator.cs b/TagCloudDi/Layouter/RectanglesGenerator.cs new file mode 100644 index 000000000..3d01e6600 --- /dev/null +++ b/TagCloudDi/Layouter/RectanglesGenerator.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using TagCloudDi.TextProcessing; + +namespace TagCloudDi.Layouter +{ + public class RectanglesGenerator(ITextProcessor textProcessor, Settings settings, ILayouter layouter) : IRectanglesGenerator + { + public IEnumerable GetRectanglesData() + { + var frequencies = textProcessor.GetWordsFrequency(); + var totalAmount = frequencies.Sum(x => x.Value); + return frequencies + .OrderByDescending(x => x.Value) + .Select(x => + { + using var font = new Font(settings.FontName, settings.FontSize * ( + x.Value * 100 / totalAmount), FontStyle.Regular); + return new RectangleData(layouter.PutNextRectangle(GetTextSize(x.Key, font)), x.Key, font.Size); + }) + .ToList(); + } + + private Size GetTextSize(string text, Font font) + { + using var temporaryBitmap = new Bitmap(1, 1); + using var temporaryGraphics = Graphics.FromImage(temporaryBitmap); + return Size.Ceiling(temporaryGraphics.MeasureString(text, font)); + } + } +} diff --git a/TagCloudDi/Program.cs b/TagCloudDi/Program.cs new file mode 100644 index 000000000..dfe29ab7f --- /dev/null +++ b/TagCloudDi/Program.cs @@ -0,0 +1,17 @@ +using Autofac; +using CommandLine; +using TagCloudDi.Applications; + +namespace TagCloudDi +{ + abstract class MainClass + { + public static void Main(string[] args) + { + var settings = Parser.Default.ParseArguments(args).Value; + var container = Container.SetupContainer(settings); + var app = container.Resolve(); + app.Run(); + } + } +} diff --git a/TagCloudDi/Settings.cs b/TagCloudDi/Settings.cs new file mode 100644 index 000000000..eeaac6521 --- /dev/null +++ b/TagCloudDi/Settings.cs @@ -0,0 +1,52 @@ +using System.Drawing.Imaging; +using CommandLine; + +namespace TagCloudDi +{ + public class Settings + { + [Option('f', "FontName", Required = false, Default = "Arial")] + public string FontName { get; set; } + + [Option('z', "FontSize", Required = false, Default = 5)] + public int FontSize { get; set; } + + [Option('t', "TextPath", Required = true)] + public string TextPath { get; set; } + + [Option('e', "ExcludedWordsPath", Required = true)] + public string ExcludedWordsPath { get; set; } + + [Option('p', "SpiralScale", Required = false, Default = 1)] + public int SpiralScale { get; set; } + + [Option('y', "ImageHeight", Required = false, Default = 1080)] + public int ImageHeight { get; set; } + + [Option('x', "ImageWidth", Required = false, Default = 1920)] + public int ImageWidth { get; set; } + + [Option('s', "SavePathWithName", Required = true)] + public string SavePath { get; set; } + + [Option('c', "TextColor", Required = false, Default = "White")] + public string TextColor { get; set; } + + [Option('b', "BackgroundColor", Required = false, Default = "Black")] + public string BackColor { get; set; } + + [Option('i', "ImageFormat", Required = false, Default = "png")] + public string ImageFormat { get; set; } + + public ImageFormat GetFormat() => ImageFormat.ToLower() switch + { + "bmp" => System.Drawing.Imaging.ImageFormat.Bmp, + "gif" => System.Drawing.Imaging.ImageFormat.Gif, + "png" => System.Drawing.Imaging.ImageFormat.Png, + "tiff" => System.Drawing.Imaging.ImageFormat.Tiff, + "jpeg" => System.Drawing.Imaging.ImageFormat.Jpeg, + _ => throw new NotImplementedException() + }; + + } +} diff --git a/TagCloudDi/TagCloudDi.csproj b/TagCloudDi/TagCloudDi.csproj new file mode 100644 index 000000000..8746ebc55 --- /dev/null +++ b/TagCloudDi/TagCloudDi.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + enable + enable + 12 + + + + + + + + + diff --git a/TagCloudDi/TextProcessing/FileTextReader.cs b/TagCloudDi/TextProcessing/FileTextReader.cs new file mode 100644 index 000000000..7014079b9 --- /dev/null +++ b/TagCloudDi/TextProcessing/FileTextReader.cs @@ -0,0 +1,19 @@ +namespace TagCloudDi.TextProcessing +{ + public class FileTextReader : ITextReader + { + public IEnumerable GetWordsFrom(string filePath) + { + var words = new List(); + using var sr = new StreamReader(filePath); + var line = sr.ReadLine(); + while (line != null) + { + words.Add(line!.ToLower()); + line = sr.ReadLine(); + } + + return words; + } + } +} diff --git a/TagCloudDi/TextProcessing/ITextProcessor.cs b/TagCloudDi/TextProcessing/ITextProcessor.cs new file mode 100644 index 000000000..a3b754449 --- /dev/null +++ b/TagCloudDi/TextProcessing/ITextProcessor.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.TextProcessing +{ + public interface ITextProcessor + { + public Dictionary GetWordsFrequency(); + } +} diff --git a/TagCloudDi/TextProcessing/ITextReader.cs b/TagCloudDi/TextProcessing/ITextReader.cs new file mode 100644 index 000000000..0c39fab64 --- /dev/null +++ b/TagCloudDi/TextProcessing/ITextReader.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.TextProcessing +{ + public interface ITextReader + { + IEnumerable GetWordsFrom(string resource); + } +} diff --git a/TagCloudDi/TextProcessing/TextProcessor.cs b/TagCloudDi/TextProcessing/TextProcessor.cs new file mode 100644 index 000000000..11a57024b --- /dev/null +++ b/TagCloudDi/TextProcessing/TextProcessor.cs @@ -0,0 +1,14 @@ +namespace TagCloudDi.TextProcessing +{ + public class TextProcessor(Settings settings, ITextReader fileTextReader) : ITextProcessor + { + public Dictionary GetWordsFrequency() + { + var excludedWords = fileTextReader.GetWordsFrom(settings.ExcludedWordsPath).ToHashSet(); + return fileTextReader.GetWordsFrom(settings.TextPath) + .Where(t => t.Length > 3 && !excludedWords.Contains(t)) + .GroupBy(x => x) + .ToDictionary(key => key.Key, amount => amount.Count()); + } + } +} diff --git a/TagCloudDi_Tests/ArchimedianSpiral_Should.cs b/TagCloudDi_Tests/ArchimedianSpiral_Should.cs new file mode 100644 index 000000000..d1f904055 --- /dev/null +++ b/TagCloudDi_Tests/ArchimedianSpiral_Should.cs @@ -0,0 +1,32 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using TagCloudDi; +using TagCloudDi.Layouter; + +namespace TagCloudDi_Tests +{ + [TestFixture] + public class ArchimedeanSpiral_Should + { + private readonly Settings settings = new() { SpiralScale = 1 }; + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void ReturnCenterPoint_WhenFirstTime_GetNextPoint(Point point) + { + var spiral = new ArchimedeanSpiral(point, settings); + spiral.GetNextPoint().Should().BeEquivalentTo(point); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), + nameof(TestDataArchimedeanSpiral.DifferentIterationsAdded_ExpectedPoints))] + public void ReturnsCorrectPoint_When(int iterations, Point expectedPoint) + { + var spiral = new ArchimedeanSpiral(new Point(), settings); + for (var i = 0; i < iterations; i++) + spiral.GetNextPoint(); + + spiral.GetNextPoint().Should().BeEquivalentTo(expectedPoint); + } + } +} diff --git a/TagCloudDi_Tests/CircularCloudLayouter_Should.cs b/TagCloudDi_Tests/CircularCloudLayouter_Should.cs new file mode 100644 index 000000000..ec818399e --- /dev/null +++ b/TagCloudDi_Tests/CircularCloudLayouter_Should.cs @@ -0,0 +1,121 @@ +using System.Drawing; +using System.Reflection; +using FluentAssertions; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using TagCloudDi; +using TagCloudDi.Layouter; + +namespace TagCloudDi_Tests +{ + class CircularCloudLayouter_Should + { + private static CircularCloudLayouter? layouter; + private static readonly Settings settings = new() { SpiralScale = 1 }; + + private string imagePath = + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\FailedLayout.png"; + + private IPointGenerator? generator; + + [TearDown] + public void TagCloudVisualizerCircularCloudLayouterTearDown() + { + if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed && layouter is not null) + { + var failImage = Test_Drawer.GetImage(new Size(1920, 1080), layouter.Rectangles); + failImage.Save(imagePath); + Console.WriteLine($"Tag cloud visualization saved to file <{imagePath}>"); + } + + layouter = null; + generator = null; + } + + [TestCaseSource(typeof(TestDataCircularCloudLayouter), + nameof(TestDataCircularCloudLayouter.ZeroOrLessHeightOrWidth_Size))] + public void Throw_WhenPutNewRectangle_WidthOrHeightLessEqualsZero(Size size) + { + generator = new ArchimedeanSpiral(new Point(), settings); + var action = new Action(() => new CircularCloudLayouter(generator).PutNextRectangle(size)); + action.Should().Throw() + .Which.Message.Should().Contain("zero or negative height or width"); + } + + [Test] + public void RectanglesEmpty_AfterCreation() + { + generator = new ArchimedeanSpiral(new Point(), settings); + layouter = new CircularCloudLayouter(generator); + layouter.Rectangles.Should().BeEmpty(); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void Add_FirstRectangle_ToCenter(Point center) + { + var generator = new ArchimedeanSpiral(center, settings); + layouter = new CircularCloudLayouter(generator); + layouter.PutNextRectangle(new Size(10, 2)); + layouter.Rectangles.Should().HaveCount(1) + .And.AllBeEquivalentTo(new Rectangle( + new Point(center.X - 10 / 2, center.Y - 2 / 2), new Size(10, 2))); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void AddSeveralRectangles_Correctly(Point centerPoint) + { + var amount = 25; + layouter = CreateLayouter_With_SeveralRectangles(amount, centerPoint); + layouter.Rectangles.Should().HaveCount(amount); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void AddSeveralRectangles_DoNotIntersect(Point centerPoint) + { + layouter = CreateLayouter_With_SeveralRectangles(25, centerPoint); + var rectangles = layouter.Rectangles; + for (var i = 1; i < rectangles.Count; i++) + rectangles.Skip(i).All(x => !rectangles[i - 1].IntersectsWith(x)).Should().Be(true); + } + + [Test] + public void DensityTest() + { + var centerPoint = new Point(960, 540); + layouter = CreateLayouter_With_SeveralRectangles(4000, centerPoint); + var rectanglesSquare = 0; + var radius = 0; + foreach (var rectangle in layouter.Rectangles) + { + rectanglesSquare += rectangle.Width * rectangle.Height; + + var x = Math.Max( + Math.Abs(centerPoint.X - rectangle.X), + rectangle.X + rectangle.Width - centerPoint.X + ); + var y = Math.Max( + Math.Abs(centerPoint.Y - rectangle.Y), + rectangle.Y + rectangle.Height - centerPoint.Y + ); + radius = Math.Max(radius, (int)Math.Sqrt(x * x + y * y)); + } + + var circleSquare = Math.PI * radius * radius; + (rectanglesSquare / circleSquare).Should().BeGreaterOrEqualTo(0.75); + } + + private static CircularCloudLayouter CreateLayouter_With_SeveralRectangles(int amount, Point center) + { + var generator = new ArchimedeanSpiral(center, settings); + var newLayouter = new CircularCloudLayouter(generator); + + for (var i = amount; i > 0; i--) + { + var width = i % 40 + 10; + newLayouter.PutNextRectangle(new Size(width, width / 5)); + } + + return newLayouter; + } + } +} \ No newline at end of file diff --git a/TagCloudDi_Tests/TagCloudDi_Tests.csproj b/TagCloudDi_Tests/TagCloudDi_Tests.csproj new file mode 100644 index 000000000..7b5678dcf --- /dev/null +++ b/TagCloudDi_Tests/TagCloudDi_Tests.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + diff --git a/TagCloudDi_Tests/TestDataArchimedeanSpiral.cs b/TagCloudDi_Tests/TestDataArchimedeanSpiral.cs new file mode 100644 index 000000000..fc7a84d2a --- /dev/null +++ b/TagCloudDi_Tests/TestDataArchimedeanSpiral.cs @@ -0,0 +1,26 @@ +using System.Drawing; +using NUnit.Framework; + +namespace TagCloudDi_Tests +{ + public class TestDataArchimedeanSpiral + { + public static IEnumerable Different_CenterPoints() + { + yield return new TestCaseData(new Point(0, 0)).SetName("(0, 0) center"); + yield return new TestCaseData(new Point(343, 868)).SetName("(343, 868) center"); + yield return new TestCaseData(new Point(960, 540)).SetName("(960, 540) center"); + } + + public static IEnumerable DifferentIterationsAdded_ExpectedPoints() + { + yield return new TestCaseData(0, new Point(0, 0)).SetName("0 iterations, central point"); + yield return new TestCaseData(90, new Point(0, (int)(Math.PI / 2))).SetName("90 iterations, half PI"); + yield return new TestCaseData(180, new Point((int)(-Math.PI), 0)).SetName("180 iterations, PI"); + yield return new TestCaseData(270, new Point(0, (int)(-Math.PI * 3 / 2))).SetName("270 iterations, 3/2 PI"); + yield return new TestCaseData(360, new Point((int)(2 * Math.PI), 0)).SetName("360 iterations, double PI"); + yield return new TestCaseData(450, new Point(0, (int)(Math.PI * 5 / 2))).SetName("450 iterations, 5/2 PI"); + yield return new TestCaseData(540, new Point((int)(-3 * Math.PI), 0)).SetName("540 iterations, triple PI"); + } + } +} diff --git a/TagCloudDi_Tests/TestDataCircularCloudLayouter.cs b/TagCloudDi_Tests/TestDataCircularCloudLayouter.cs new file mode 100644 index 000000000..7b792ed4f --- /dev/null +++ b/TagCloudDi_Tests/TestDataCircularCloudLayouter.cs @@ -0,0 +1,19 @@ +using System.Drawing; +using NUnit.Framework; + +namespace TagCloudDi_Tests +{ + public class TestDataCircularCloudLayouter + { + public static IEnumerable ZeroOrLessHeightOrWidth_Size() + { + yield return new TestCaseData(new Size(0, 1)).SetName("Zero_Width_Size"); + yield return new TestCaseData(new Size(1, 0)).SetName("Zero_Height_Size"); + yield return new TestCaseData(new Size(0, 0)).SetName("Zero_Width_And_Height_Size"); + yield return new TestCaseData(new Size(int.MinValue, 1)).SetName("Negative_Width_Size"); + yield return new TestCaseData(new Size(1, int.MinValue)).SetName("Negative_Height_Size"); + yield return new TestCaseData(new Size(int.MinValue, int.MinValue)) + .SetName("Negative_Width_And_Height_Size"); + } + } +} diff --git a/TagCloudDi_Tests/TestDrawer.cs b/TagCloudDi_Tests/TestDrawer.cs new file mode 100644 index 000000000..8990b2701 --- /dev/null +++ b/TagCloudDi_Tests/TestDrawer.cs @@ -0,0 +1,25 @@ +using System.Drawing; + +namespace TagCloudDi_Tests +{ + public class Test_Drawer + { + public static Image GetImage(Size size, IEnumerable rectangles) + { + if (size.Width <= 0 || size.Height <= 0) + throw new ArgumentException("size width and height should be positive", "size"); + var image = new Bitmap(size.Width, size.Height); + using (var gr = Graphics.FromImage(image)) + { + gr.Clear(Color.Black); + using (var brush = new SolidBrush(Color.White)) + { + foreach (var rectangle in rectangles) + gr.FillRectangle(brush, rectangle); + } + } + + return image; + } + } +} diff --git a/di.sln b/di.sln index b27b7c05d..0c715f015 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}") = "TagCloudDi", "TagCloudDi\TagCloudDi.csproj", "{1369275F-4DD2-44F5-9F5E-C43DE5BE75A7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloudDi_Tests", "TagCloudDi_Tests\TagCloudDi_Tests.csproj", "{5B585A67-9F41-4C87-ADEE-9978275048B1}" +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 + {1369275F-4DD2-44F5-9F5E-C43DE5BE75A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1369275F-4DD2-44F5-9F5E-C43DE5BE75A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1369275F-4DD2-44F5-9F5E-C43DE5BE75A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1369275F-4DD2-44F5-9F5E-C43DE5BE75A7}.Release|Any CPU.Build.0 = Release|Any CPU + {5B585A67-9F41-4C87-ADEE-9978275048B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B585A67-9F41-4C87-ADEE-9978275048B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B585A67-9F41-4C87-ADEE-9978275048B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B585A67-9F41-4C87-ADEE-9978275048B1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal