-
Notifications
You must be signed in to change notification settings - Fork 307
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
base: master
Are you sure you want to change the base?
Черных Илья #243
Changes from all commits
9590cf2
ef5b9d0
c3361d0
9e42a71
bd1809c
0747ad9
f792052
c17e0f9
09efbed
2b2988d
5e32c12
7aa664f
42db42a
07fd1a3
0bd2619
2e4a6c9
f92ef27
5c9d9b7
353a7f5
21dfe52
66bfc9e
492bdc1
19ccfbf
ad8646b
e736a26
66c278f
61330d3
c35b934
d9abf43
3274439
d3f04e6
44309c3
938e8d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
} | ||
} | ||
} |
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) | ||
{ | ||
var stepIncreasingRadius = 2; | ||
var center = new Point(0, 0); | ||
var circularLayoutAlgorithm = new CircularLayoutAlgorithm( | ||
center, | ||
stepIncreasingRadius: stepIncreasingRadius, | ||
stepIncreasingAngle: stepIncreasingAngle | ||
); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} | ||
} |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
} | ||
} |
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(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
![Визуализация десяти прямоугольников](images/visualization10Rectangles.png) | ||
![Визуализация пятидесяти прямоугольников](images/visualization50Rectangles.png) | ||
![Визуализация ста прямоугольников](images/visualization100Rectangles.png) |
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мне не очень нравится название теста.
Во-первых, тут есть магическое число 3, и без знания реализации алгоритма не понятно почему оно именно такое
Во-вторых, тут довольно слабые проверки на неравенство и больше/меньше. Мы по идее знаем, как должен работать алгоритм, соответственно мы можем написать более точные ассерты. Мне кажется, что раз есть возможность, то лучше сравнивать прям покоординатно точки, а возможность есть, т.к. мы шаг для радиуса и угла прям в конструктор в класса проверяемого передаем.
Т.е. мы можем так же получить три точки и проверить каждую на точное соответствие ожидаемой координате.