Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Черных Илья #243

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9590cf2
Create projects TagsCloudVisualization, Tests
Ilya4rh Nov 12, 2024
ef5b9d0
Add tdd.sln, tdd.sln.DotSettings
Ilya4rh Nov 12, 2024
c3361d0
Add ICircularCloudLayouter.cs, CircularCloudLayouter.cs
Ilya4rh Nov 12, 2024
9e42a71
Add CircularCloudLayouterTests.cs
Ilya4rh Nov 12, 2024
bd1809c
Реализация добавления первого прямоугольника
Ilya4rh Nov 12, 2024
0747ad9
Изменение в добавлении первого прямоугольника
Ilya4rh Nov 12, 2024
f792052
Добавлена реализация добавления нескольких прямоугольников по спирали…
Ilya4rh Nov 12, 2024
c17e0f9
Убрал отдельную логику добавления первого прямоугольника (излишняя)
Ilya4rh Nov 12, 2024
09efbed
Добавил тест на расположение по спирали
Ilya4rh Nov 12, 2024
2b2988d
Добавил отрисовку получившегося результата
Ilya4rh Nov 12, 2024
5e32c12
Добавил изображения и README.md файл с ним
Ilya4rh Nov 12, 2024
7aa664f
Поправил путь к изображениям в файле README.md
Ilya4rh Nov 12, 2024
42db42a
Поправил README.md
Ilya4rh Nov 12, 2024
07fd1a3
Поправил CircularCloudLayouterTests.cs, добавил Setup
Ilya4rh Nov 12, 2024
0bd2619
Добавил TearDown с визуализацией изображения сломанных тестов
Ilya4rh Nov 12, 2024
2e4a6c9
Поправил класс с визуализацией
Ilya4rh Nov 13, 2024
f92ef27
Добавил интерфейс алгоритма раскладки и класс раскладки по кругу
Ilya4rh Nov 16, 2024
5c9d9b7
Перенес реализацию раскладки по кругу в новый класс
Ilya4rh Nov 16, 2024
353a7f5
Изменил класс CircularCloudLayouter и тесты с появлением нового интер…
Ilya4rh Nov 16, 2024
21dfe52
Изменил название проекта с тестами, класса с тестами и методов внутри
Ilya4rh Nov 16, 2024
66bfc9e
Разделил отрисовку и сохранение изображений
Ilya4rh Nov 16, 2024
492bdc1
Изменил название интерфейса алгоритмов раскладки и сопутствующих классов
Ilya4rh Nov 16, 2024
19ccfbf
Вынес логику вычисления следующей точки расположения
Ilya4rh Nov 17, 2024
ad8646b
Изменил ограничение в конструкторе алгоритма
Ilya4rh Nov 17, 2024
e736a26
Добавил радиус в алгоритм
Ilya4rh Nov 17, 2024
66c278f
Изменил название проекта с тестами
Ilya4rh Nov 17, 2024
61330d3
Изменил тесты расстановки прямоугольников, добавил класс с утилитами
Ilya4rh Nov 17, 2024
c35b934
Изменил метод добавления прямоугольника
Ilya4rh Nov 17, 2024
d9abf43
Добавил новые классы для работы с изображением
Ilya4rh Nov 17, 2024
3274439
Перенес логику отрисовки и сохранения в новые классы
Ilya4rh Nov 17, 2024
d3f04e6
Убрал точку центра из алгоритма расстановки, перенес генерацию прямоу…
Ilya4rh Nov 18, 2024
44309c3
Добавил тесты на алгоритм по расчету точки
Ilya4rh Nov 18, 2024
938e8d8
Правки в классах работы с изображениями
Ilya4rh Nov 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions cs/TagsCloudVisiualizationTests/CircularCloudLayoutTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Drawing;
using System.Drawing.Imaging;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using TagsCloudVisiualizationTests.Utils;
using TagsCloudVisualization;
using TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;
using TagsCloudVisualization.Visualization;

namespace TagsCloudVisiualizationTests;

[TestFixture]
public class CircularCloudLayoutTests
{
private ICircularCloudLayouter cloudLayouter;
private List<Rectangle> addedRectangles;

[SetUp]
public void Setup()
{
var center = new Point(0, 0);
cloudLayouter = new CircularCloudLayouter(new CircularLayoutAlgorithm(center));
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 = ImageDrawer.DrawLayout(addedRectangles, 10);

ImageSaver.Save(bitmap, pathImageStored, $"{testName}.png", ImageFormat.Png);

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);
}
}
}
91 changes: 91 additions & 0 deletions cs/TagsCloudVisiualizationTests/CircularLayoutAlgorithmTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Drawing;
using FluentAssertions;
using NUnit.Framework;
using TagsCloudVisiualizationTests.Utils;
using TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

namespace TagsCloudVisiualizationTests;

[TestFixture]
public class CircularLayoutAlgorithmTests
{
[TestCase(0)]
[TestCase(-5)]
public void Constructor_ShouldArgumentException_WhenStepIncreasingRadiusHasInvalidValue(int stepIncreasingRadius)
{
var action = () =>
{
_ = new CircularLayoutAlgorithm(new Point(0, 0), stepIncreasingRadius: stepIncreasingRadius);
};

action
.Should()
.Throw<ArgumentException>()
.WithMessage("The parameter 'stepIncreasingRadius' is less than or equal to zero");
}

[Test]
public void Constructor_ShouldArgumentException_WhenStepIncreasingAngleIsZero()
{
var action = () =>
{
_ = new CircularLayoutAlgorithm(new Point(0, 0), stepIncreasingAngle: 0);
};

action
.Should()
.Throw<ArgumentException>()
.WithMessage("The parameter 'stepIncreasingAngle' is zero");
}

[TestCase(0, 0)]
[TestCase(-4, 5)]
public void CalculateNextPoint_ShouldPointIsCenter_WhenCalculateFirstPoint(int centerCoordinateX, int centerCoordinateY)
{
var center = new Point(centerCoordinateY, centerCoordinateY);
var circularLayoutAlgorithm = new CircularLayoutAlgorithm(center);

var nextPoint = circularLayoutAlgorithm.CalculateNextPoint();

nextPoint.Should().Be(center);
}

[TestCase(2)]
[TestCase(7)]
public void CalculateNextPoint_ShouldIncreaseRadius_WhenCalculateTwoPoints(int stepIncreasingRadius)
{
var circularLayoutAlgorithm =
new CircularLayoutAlgorithm(new Point(0, 0), stepIncreasingRadius: stepIncreasingRadius);

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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне не очень нравится название теста.
Во-первых, тут есть магическое число 3, и без знания реализации алгоритма не понятно почему оно именно такое
Во-вторых, тут довольно слабые проверки на неравенство и больше/меньше. Мы по идее знаем, как должен работать алгоритм, соответственно мы можем написать более точные ассерты. Мне кажется, что раз есть возможность, то лучше сравнивать прям покоординатно точки, а возможность есть, т.к. мы шаг для радиуса и угла прям в конструктор в класса проверяемого передаем.
Т.е. мы можем так же получить три точки и проверить каждую на точное соответствие ожидаемой координате.

{
var stepIncreasingRadius = 2;
var center = new Point(0, 0);
var circularLayoutAlgorithm = new CircularLayoutAlgorithm(
center,
stepIncreasingRadius: stepIncreasingRadius,
stepIncreasingAngle: stepIncreasingAngle
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мелочь. Скобочка далеко уехала куда-то, ее не нужно отдельно переносить


_ = 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="4.2.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TagsCloudVisualization\TagsCloudVisualization.csproj" />
</ItemGroup>

</Project>
40 changes: 40 additions & 0 deletions cs/TagsCloudVisiualizationTests/Utils/GeometryUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Drawing;

namespace TagsCloudVisiualizationTests.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<Size> GenerateRectangleSizes(int countRectangles, int minSideLength, int maxSideLength)
{
var random = new Random();

var generatedSizes = Enumerable.Range(0, countRectangles)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мелочь. В Linq цепочке лучше все элементы переносить на новую строчку, включая первый

.Select(_ => new Size(
random.Next(minSideLength, maxSideLength),
random.Next(minSideLength, maxSideLength))
)
.ToList();

return generatedSizes;
}
}
39 changes: 39 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Drawing;
using TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

namespace TagsCloudVisualization;

public class CircularCloudLayouter : ICircularCloudLayouter
{
private readonly ILayoutAlgorithm layoutAlgorithm;
private readonly List<Rectangle> addedRectangles = [];

public CircularCloudLayouter(ILayoutAlgorithm layoutAlgorithm)
{
this.layoutAlgorithm = layoutAlgorithm;
}

public Rectangle PutNextRectangle(Size rectangleSize)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сейчас твой метод кладет прямоугольники всегда одним и тем же способом, но в идеале было бы хорошо алгоритм раскладки вынести куда-то в сторону и передавать его в конструктор, чтобы разные экземпляры CircularCloudLayouter могли раскладывать разными способами. Конкретно в этой задаче способ нужен только один, но на будущее лучше сделать класс гибче, и чтобы он почти ничего не знал о том, по какому алгоритму выбираются точки для прямоугольников

{
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));
}
}
8 changes: 8 additions & 0 deletions cs/TagsCloudVisualization/ICircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Drawing;

namespace TagsCloudVisualization;

public interface ICircularCloudLayouter
{
Rectangle PutNextRectangle(Size rectangleSize);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Drawing;

namespace TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

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 OneDegree = Math.PI / 180;
private const double FullCircleRotation = 2 * Math.PI;

public CircularLayoutAlgorithm(Point center, double stepIncreasingAngle = OneDegree, int stepIncreasingRadius = 1)
{
if (stepIncreasingRadius <= 0)
throw new ArgumentException("The parameter 'stepIncreasingRadius' is less than or equal to zero");
if (stepIncreasingAngle == 0)
throw new ArgumentException("The parameter 'stepIncreasingAngle' is zero");

this.center = center;
this.stepIncreasingAngle = stepIncreasingAngle;
this.stepIncreasingRadius = 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Drawing;

namespace TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

public interface ILayoutAlgorithm
{
Point CalculateNextPoint();
}
3 changes: 3 additions & 0 deletions cs/TagsCloudVisualization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
![Визуализация десяти прямоугольников](images/visualization10Rectangles.png)
![Визуализация пятидесяти прямоугольников](images/visualization50Rectangles.png)
![Визуализация ста прямоугольников](images/visualization100Rectangles.png)
17 changes: 17 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="images\" />
</ItemGroup>

</Project>
Loading