Skip to content

Commit

Permalink
Поправил замечания, дописал интерфейс, добавил несколько тестов под г…
Browse files Browse the repository at this point in the history
…енератор точек, создал readme, перенес тесты из отдельного проекта в основной
  • Loading branch information
Alex-Vay committed Nov 15, 2024
1 parent 8aa0286 commit cd62acd
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 141 deletions.
34 changes: 19 additions & 15 deletions cs/TagsCloudVisualization/CloudLayouter/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,46 @@ namespace TagsCloudVisualization.CloudLayouter;

public class CircularCloudLayouter : ICircularCloudLayouter
{
private Point center;
private List<Rectangle> rectangles = [];
private SpiralPointsGenerator spiral;
private double step = Constans.LayoutStep;
private double angleOffset = Constans.LayoutAngleOffset;
public Point Center { get; }
public List<Rectangle> GeneratedRectangles { get; }
private IPointsGenerator spiral { get; }

public CircularCloudLayouter(Point center)
{
this.center = center;
Center = center;
GeneratedRectangles = new List<Rectangle>();
spiral = new SpiralPointsGenerator(center);
}

public CircularCloudLayouter(Point center, int step, int angleOffset)
{
GeneratedRectangles = new List<Rectangle>();
spiral = new SpiralPointsGenerator(center, step, angleOffset);
}

public Rectangle PutNextRectangle(Size rectangleSize)
{
if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0)
throw new ArgumentException($"{nameof(rectangleSize)} height and width must be greater than zero");
var rectangle = GetNextRectangle(rectangleSize);
while (rectangles.Any(rectangle.IntersectsWith)) //ìîæíî èñïîëüçîâàòü do while
Rectangle rectangle;
do
{
rectangle = GetNextRectangle(rectangleSize);
rectangles.Add(rectangle);
} while (GeneratedRectangles.Any(rectangle.IntersectsWith));
GeneratedRectangles.Add(rectangle);
return rectangle;
}

private Rectangle GetNextRectangle(Size rectagleSize)
private Rectangle GetNextRectangle(Size rectangleSize)
{
var rectanglePosition = spiral.GetNextPointPosition();
var centerOfRectangle = CreateRectangleWithCenter(rectanglePosition, rectagleSize);
return centerOfRectangle;
return CreateRectangle(rectanglePosition, rectangleSize);
}

private Rectangle CreateRectangleWithCenter(Point center, Size rectangleSize)
private static Rectangle CreateRectangle(Point center, Size rectangleSize)
{
var x = center.X - rectangleSize.Width / 2;
var y = center.Y - rectangleSize.Height / 2;
return new Rectangle(x, y, rectangleSize.Width, rectangleSize.Height);
}

public List<Rectangle> GetRectangles() => rectangles;
}
20 changes: 0 additions & 20 deletions cs/TagsCloudVisualization/CloudLayouter/CloudGenerator.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Drawing;

namespace TagsCloudVisualization.CloudLayouter;

public static class ICircularCloudLayouterExtensions
{
public static void GenerateCloud(this ICircularCloudLayouter cloudLayouter,
int rectanglesNumber = 1000,
int minRectangleSize = 10,
int maxRectangleSize = 50)
{
var random = new Random();
new Rectangle[rectanglesNumber]
.Select(x => new Size(
random.Next(minRectangleSize, maxRectangleSize),
random.Next(minRectangleSize, maxRectangleSize)))
.Select(size => cloudLayouter.PutNextRectangle(size))
.ToArray();
}
}
14 changes: 0 additions & 14 deletions cs/TagsCloudVisualization/Constans.cs

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Drawing;

namespace TagsCloudVisualization.PointsGenerators;

public interface IPointsGenerator
{
Point GetNextPointPosition();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

namespace TagsCloudVisualization.PointsGenerators;

public class SpiralPointsGenerator
public class SpiralPointsGenerator : IPointsGenerator
{
private double angleOffset;
private readonly double step = 0.1;
private readonly double angleOffset = 0.1;
private readonly Point center;
private readonly double step;
private double angle = 0;

public SpiralPointsGenerator(Point center) => this.center = center;

public SpiralPointsGenerator(Point center, double step, double angleOffset)
{
if (step == 0 || angleOffset == 0)
throw new ArgumentException($"Step and angleOffset must not be zero");
this.center = center;
this.step = step;
this.angleOffset = angleOffset;
Expand Down
12 changes: 9 additions & 3 deletions cs/TagsCloudVisualization/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ namespace TagsCloudVisualization;

public static class Program
{
private const int imageWidth = 1500;
private const int imageHeight = 1500;

public static void Main()
{
var cloudLayouter = CloudGenerator.GenerateCloud();
var rectangles = cloudLayouter.GetRectangles();
var imageSize = new Size(imageWidth, imageHeight);
var center = new Point(imageSize.Width / 2, imageSize.Height / 2);
var layouter = new CircularCloudLayouter(center);
layouter.GenerateCloud();
var rectangles = layouter.GeneratedRectangles;
var visualizer = new SimpleCloudVisualizer();
visualizer.CreateBitmap(rectangles, new Size(Constans.ImageWidth, Constans.ImageHeight));
visualizer.CreateBitmap(rectangles, imageSize);
}
}
4 changes: 4 additions & 0 deletions cs/TagsCloudVisualization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
1000 прямоугольников шаг 0.1 угловое смещение 0.1 <img scr="Images\1000_0.1_0.1_TagCloud.jpg">
1000 прямоугольников шаг 0.01 угловое смещение 0.01 <img scr="Images\1000_0.01_0.01_TagCloud.jpg">
1000 прямоугольников шаг 1 угловое смещение 1 <img scr="Images\1000_1_1_TagCloud.jpg">
1000 прямоугольников шаг 10 угловое смещение 0.01 <img scr="Images\1000_10_0.01_TagCloud.jpg">
5 changes: 5 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<BaseOutputPath></BaseOutputPath>
<StartupObject>TagsCloudVisualization.Program</StartupObject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
using NUnit.Framework.Interfaces;
using NUnit.Framework.Interfaces;
using FluentAssertions;
using System.Drawing;
using TagsCloudVisualization.CloudLayouter;
using TagsCloudVisualization.Visualizers;
using TagsCloudVisualization;
using NUnit.Framework;

namespace TagsCloudVisualizationTests.CircularCloudLayouterTest;
namespace TagsCloudVisualization.Tests.CircularCloudLayouterTests;

[TestFixture]
[TestFixture, Parallelizable]
public class CircularCloudLayouterTests
{
private List<Rectangle> rectanglesInTest;
private CircularCloudLayouter cloudLayouter;
private const int imageWidth = 1500;
private const int imageHeight = 1500;

[SetUp]
public void Init()
{
var center = new Point(imageWidth / 2, imageHeight / 2);
cloudLayouter = new CircularCloudLayouter(center);
cloudLayouter.GenerateCloud(100);
}

[TearDown]
public void TearDown()
Expand All @@ -20,83 +30,74 @@ public void TearDown()
var directory = "FailedVisualisations";
var path = Path.Combine(directory, $"{TestContext.CurrentContext.Test.Name}_visualisation.png");
var visuliser = new SimpleCloudVisualizer();
visuliser.CreateBitmap(rectanglesInTest, new(Constans.ImageWidth, Constans.ImageHeight), directory, path);
visuliser.CreateBitmap(cloudLayouter.GeneratedRectangles, new(imageWidth, imageHeight), path);
Console.WriteLine($"Tag cloud visualization saved to file {path}");
}

[TestCase(0, 1, TestName = "WhenWidthIsZero")]
[TestCase(1, 0, TestName = "WhenHeightIsZero")]
[TestCase(-1, 1, TestName = "WhenWidthIsNegative")]
[TestCase(1, -1, TestName = "WhenHeightIsNegative")]
public void LayouterPutNextRectangle_ShouldThrowArgumentException(int width, int height)
public void PutNextRectangle_ShouldThrowArgumentException(int width, int height)
{
var layouter = new CircularCloudLayouter(new(0, 0));
var size = new Size(width, height);

var action = () => layouter.PutNextRectangle(size);
var action = () => cloudLayouter.PutNextRectangle(size);

action.Should().Throw<ArgumentException>();
}

[Test]
public void FirstRectang_ShouldBeInCenter()
public void PutNextRectangle_FirstRectangle_ShouldBeInCenter()
{
var layouter = new CircularCloudLayouter(new(0, 0));
cloudLayouter = new CircularCloudLayouter(cloudLayouter.Center);
var rectangleSize = new Size(10, 10);

var actualRectangle = layouter.PutNextRectangle(rectangleSize);
var expectedRectangle = new Rectangle(
-rectangleSize.Width / 2,
-rectangleSize.Height / 2,
cloudLayouter.Center.X - rectangleSize.Width / 2,
cloudLayouter.Center.Y - rectangleSize.Height / 2,
rectangleSize.Width,
rectangleSize.Height
);
rectanglesInTest = layouter.GetRectangles();

var actualRectangle = cloudLayouter.PutNextRectangle(rectangleSize);

actualRectangle.Should().BeEquivalentTo(expectedRectangle);
}

[Test]
[Repeat(10)]
public void Rectangles_ShouldNotHaveIntersects()
{
var layouter = CloudGenerator.GenerateCloud(100);
rectanglesInTest = layouter.GetRectangles();

var expectedResult = AreRectanglesHaveIntersects(rectanglesInTest);

expectedResult.Should().BeFalse();
}
public void PutNextRectangle_Rectangles_ShouldNotHaveIntersects() =>
AreRectanglesHaveIntersects(cloudLayouter.GeneratedRectangles).Should().BeFalse();

[Test]
[Repeat(10)]
public void AllRectanglesCenter_ShoulBeLikeInitCenter()
public void PutNextRectangle_AllRectanglesCenter_ShoulBeLikeInitCenter()
{
var center = new Point(Constans.ImageWidth / 2, Constans.ImageHeight / 2);
var treshold = Constans.MaxRectangleSize / 2;
var layouter = CloudGenerator.GenerateCloud(100);
rectanglesInTest = layouter.GetRectangles();
var center = cloudLayouter.Center;
var minRectangleSize = 1;
var maxRectangleSize = 10;
var treshold = maxRectangleSize / 2;

var actualCenter = GetCenterOfAllRectangles(rectanglesInTest);
cloudLayouter.GenerateCloud(100, 10, maxRectangleSize);

var actualCenter = GetCenterOfAllRectangles(cloudLayouter.GeneratedRectangles);
actualCenter.X.Should().BeInRange(center.X - treshold, center.X + treshold);
actualCenter.Y.Should().BeInRange(center.Y - treshold, center.Y + treshold);
}

[Test]
[Repeat(10)]
public void RectanglesDensity_ShouldBeMax()
public void PutNextRectangle_RectanglesDensity_ShouldBeMax()
{
var expectedDensity = 0.5;
var center = new Point(Constans.ImageWidth / 2, Constans.ImageHeight / 2);
var layouter = CloudGenerator.GenerateCloud(100);
rectanglesInTest = layouter.GetRectangles();
var expectedDensity = 0.45;
var center = cloudLayouter.Center;
var rectangles = cloudLayouter.GeneratedRectangles;

var rectanglesArea = rectanglesInTest.Sum(rect => rect.Width * rect.Height);
var radius = GetMaxDistanceBetweenRectangleAndCenter(rectanglesInTest);
var rectanglesArea = rectangles.Sum(rect => rect.Width * rect.Height);

var radius = GetMaxDistanceBetweenRectangleAndCenter(rectangles);
var circleArea = Math.PI * radius * radius;
var density = rectanglesArea / circleArea;

density.Should().BeGreaterThanOrEqualTo(expectedDensity);
}

Expand Down Expand Up @@ -130,9 +131,9 @@ private double GetMaxDistanceBetweenRectangleAndCenter(List<Rectangle> rectangle
return maxDistance;
}



private bool AreRectanglesHaveIntersects(List<Rectangle> rectangles)

private static bool AreRectanglesHaveIntersects(List<Rectangle> rectangles)
{
for (var i = 0; i < rectangles.Count; i++)
for (var j = i + 1; j < rectangles.Count; j++)
Expand All @@ -141,6 +142,6 @@ private bool AreRectanglesHaveIntersects(List<Rectangle> rectangles)
return false;
}

private double GetDistanceBetweenPoints(Point point1, Point point2)
private static double GetDistanceBetweenPoints(Point point1, Point point2)
=> Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2));
}
Loading

0 comments on commit cd62acd

Please sign in to comment.