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

Дмитрий Леонтьев #248

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
15 changes: 15 additions & 0 deletions cs/TagCloudConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Drawing;
using TagsCloudVisualization.CloudLayouter;
using TagsCloudVisualization.Draw;
using TagsCloudVisualization.Extension;
using TagsCloudVisualization.RectangleGenerator;
using TagsCloudVisualization.Saver;

var center = new Point(0, 0);
var randomRectangles = RectangleGenerator.GenerateRandomRectangles(1000);
var layouter = new CircularCloudLayouter(center);
layouter.PutRectangles(randomRectangles);
var drawer = new RectangleDraftsman(1500, 1500);
const string filename = "CloudRectangles1000.png";
drawer.CreateImage(layouter.Rectangles);
drawer.SaveImageToFile(filename);
15 changes: 15 additions & 0 deletions cs/TagCloudConsoleApp/TagCloudConsoleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

</Project>
227 changes: 227 additions & 0 deletions cs/TagsCloudVisualization.Tests/CircularCloudLayouterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
using System.Drawing;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using TagsCloudVisualization.CloudLayouter;
using TagsCloudVisualization.Draw;
using TagsCloudVisualization.Extension;
using TagsCloudVisualization.RectangleGenerator;
using TagsCloudVisualization.Saver;

namespace TagsCloudVisualizationTests;

[TestFixture]
public class CircularCloudLayouterTest()
{
private IReadOnlyList<Rectangle> rectanglesForCrashTest;

[TearDown]
public void TearDown()
{
if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed)

Choose a reason for hiding this comment

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

Молодец, что разобрался с этим)

{
var drawer = new RectangleDraftsman(1500, 1500);
var filename = $"{TestContext.CurrentContext.WorkDirectory}\\{TestContext.CurrentContext.Test.Name}.png";
drawer.CreateImage(rectanglesForCrashTest);
drawer.SaveImageToFile(filename);

Console.WriteLine($"Tag cloud visualization saved to file {filename}");
}
}

[Test]
public void PutNextRectangle_PlaceFirstRectangleAtCenter()
{
var center = new Point(1, 1);
var layouter = new CircularCloudLayouter(center);
var nextRectangle = layouter.PutNextRectangle(new Size(10, 10));

rectanglesForCrashTest = layouter.Rectangles;
nextRectangle.GetCenter().Should().Be(center);
}

[TestCase(0, 0)]
[TestCase(-1, -1)]
public void PutNextRectangle_WhenIncorrectSize_Throw(int sizeX, int sizeY)
{
var center = new Point(0, 0);
var layouter = new CircularCloudLayouter(center);

Action action = () => layouter.PutNextRectangle(new Size(sizeX, sizeY));
action.Should().Throw<ArgumentException>().WithMessage("Width and height should be greater than zero.");
}

[TestCase(1, 1, 10)]
public void PutNextRectangle_AddRectangles(int sizeX, int sizeY, int count)
{
var center = new Point(0, 0);
var layouter = new CircularCloudLayouter(center);

for (var i = 0; i < count; i++)
{
layouter.PutNextRectangle(new Size(sizeX, sizeY));
}

rectanglesForCrashTest = layouter.Rectangles;
layouter.Rectangles.Count.Should().Be(count);
}

[TestCase(30)]
[TestCase(50)]
[TestCase(100)]
[TestCase(1000)]
public void PutNextRectangle_CreateLayoutWithoutIntersections(int countRectangles)
{
var center = new Point(0, 0);
var layouter = new CircularCloudLayouter(center);
var rectangles = RectangleGenerator.GenerateRandomRectangles(countRectangles).ToList();
layouter.PutRectangles(rectangles);
rectanglesForCrashTest = layouter.Rectangles;

for (var i = 0; i < rectangles.Count; i++)
{
for (var j = i + 1; j < rectangles.Count; j++)
{
layouter.Rectangles[i].IntersectsWith(layouter.Rectangles[j]).Should().BeFalse();
}
}
}

[TestCase(0, 0, 10, Description = "Center at the center of the coordinate axis")]
[TestCase(0, 0, 100, Description = "Center at the center of the coordinate axis")]
[TestCase(0, 0, 1000, Description = "Center at the center of the coordinate axis")]
[TestCase(100, 100, 10, Description = "Center in the first quadrant of the coordinate axis")]
[TestCase(100, 100, 100, Description = "Center in the first quadrant of the coordinate axis")]
[TestCase(100, 100, 1000, Description = "Center in the first quadrant of the coordinate axis")]
[TestCase(-100, 100, 10, Description = "Center in the second quadrant of the coordinate axis")]
[TestCase(-100, 100, 100, Description = "Center in the second quadrant of the coordinate axis")]
[TestCase(-100, 100, 1000, Description = "Center in the second quadrant of the coordinate axis")]
[TestCase(-100, -100, 10, Description = "Center in the third quadrant of the coordinate axis")]
[TestCase(-100, -100, 100, Description = "Center in the third quadrant of the coordinate axis")]
[TestCase(-100, -100, 1000, Description = "Center in the third quadrant of the coordinate axis")]
[TestCase(-100, 100, 10, Description = "Center in the fourth quadrant of the coordinate axis")]
[TestCase(-100, 100, 100, Description = "Center in the fourth quadrant of the coordinate axis")]
[TestCase(-100, 100, 1000, Description = "Center in the fourth quadrant of the coordinate axis")]
public void PutNextRectangle_CreateLaoyoutWithMore40PercentRoundLayout_OnRandomNumberRectangles(
int xCenter,
int yCenter,
int countRectangles)
{
var center = new Point(xCenter, yCenter);
var randomRectangles = RectangleGenerator.GenerateRandomRectangles(countRectangles);
var layouter = new CircularCloudLayouter(center);
layouter.PutRectangles(randomRectangles);

var maxX = GetMaxX(layouter.Rectangles);
var minX = GetMinX(layouter.Rectangles);
var maxY = GetMaxY(layouter.Rectangles);
var minY = GetMinY(layouter.Rectangles);

// Вычитаю центр, чтобы сместить нашу окружность в начало координат
var radius = Max(maxX - center.X, minX - center.X, maxY - center.Y, minY - center.Y);

var deviationMaxXFromRadius = Math.Abs(radius - maxX);
var deviationMinXFromRadius = Math.Abs(radius - minX);
var deviationMaxYFromRadius = Math.Abs(radius - maxY);
var deviationMinYFromRadius = Math.Abs(radius - minY);
var numberOfPointsToMeasure = 4.0;
var averageSizeOfRectangles = layouter.Rectangles.Average(r => r.Height * r.Width);

var percentageOfRoundLayout =
1 - (deviationMaxXFromRadius + deviationMinXFromRadius + deviationMaxYFromRadius +
deviationMinYFromRadius) / numberOfPointsToMeasure / averageSizeOfRectangles;

percentageOfRoundLayout.Should().BeInRange(0.4, 1);
}

[TestCase(0, 0, 10, Description = "Center at the center of the coordinate axis")]
[TestCase(0, 0, 100, Description = "Center at the center of the coordinate axis")]
[TestCase(0, 0, 1000, Description = "Center at the center of the coordinate axis")]
[TestCase(100, 100, 10, Description = "Center in the first quadrant of the coordinate axis")]
[TestCase(100, 100, 100, Description = "Center in the first quadrant of the coordinate axis")]
[TestCase(100, 100, 1000, Description = "Center in the first quadrant of the coordinate axis")]
[TestCase(-100, 100, 10, Description = "Center in the second quadrant of the coordinate axis")]
[TestCase(-100, 100, 100, Description = "Center in the second quadrant of the coordinate axis")]
[TestCase(-100, 100, 1000, Description = "Center in the second quadrant of the coordinate axis")]
[TestCase(-100, -100, 10, Description = "Center in the third quadrant of the coordinate axis")]
[TestCase(-100, -100, 100, Description = "Center in the third quadrant of the coordinate axis")]
[TestCase(-100, -100, 1000, Description = "Center in the third quadrant of the coordinate axis")]
[TestCase(-100, 100, 10, Description = "Center in the fourth quadrant of the coordinate axis")]
[TestCase(-100, 100, 100, Description = "Center in the fourth quadrant of the coordinate axis")]
[TestCase(-100, 100, 1000, Description = "Center in the fourth quadrant of the coordinate axis")]
public void PutNextRectangle_CreateLaoyoutWithOver75PercentDensity_OnRandomNumberRectangles(
int xCenter,
int yCenter,
int countRectangles)
{
var center = new Point(xCenter, yCenter);
var randomRectangles = RectangleGenerator.GenerateRandomRectangles(countRectangles);
var layouter = new CircularCloudLayouter(center);
layouter.PutRectangles(randomRectangles);
rectanglesForCrashTest = layouter.Rectangles;

var distancesFromCenterToRectangles = GetDistancesFromCenterToRectangles(layouter.Rectangles, center);
var averageDistanceFromCenterToRectangles = distancesFromCenterToRectangles.Average();
var radiusOfCircleAroundRectangles = GetMostDistantFromCenterToRectangles(layouter.Rectangles, center);
var areaOfCircle = GetAreaOfCircle(radiusOfCircleAroundRectangles);
var areaOfRectangles = GetAreaOfRectangles(layouter.Rectangles);
var densityCoefficient = GetDensityCoefficient(areaOfRectangles, areaOfCircle,
averageDistanceFromCenterToRectangles, radiusOfCircleAroundRectangles);

densityCoefficient.Should().BeLessThan(0.36);
}

private static IEnumerable<double> GetDistancesFromCenterToRectangles(
IEnumerable<Rectangle> rectangles,
Point center) =>
rectangles
.Select(r => Math.Sqrt(Math.Pow(r.Location.X - center.X, 2) + Math.Pow(r.Location.Y - center.Y, 2)));

private static double GetMostDistantFromCenterToRectangles(IEnumerable<Rectangle> rectangles, Point center)
{
var mostDistantToLeftTopRectangles = rectangles
.Select(r => Math.Max(Math.Abs(r.Location.X - center.X),
Math.Abs(r.Location.Y - center.Y)))
.Max();
var mostDistantToRightTopRectangles = rectangles
.Select(r => Math.Max(Math.Abs(r.Location.X + r.Width - center.X),
Math.Abs(r.Location.Y - center.Y)))
.Max();
var mostDistantToLeftBottomRectangles = rectangles
.Select(r => Math.Max(Math.Abs(r.Location.X - center.X),
Math.Abs(r.Location.Y - r.Height - center.Y)))
.Max();
var mostDistantToRightBottomRectangles = rectangles
.Select(r => Math.Max(Math.Abs(r.Location.X + r.Width - center.X),
Math.Abs(r.Location.Y - r.Height - center.Y)))
.Max();

var mostDistant = Max(mostDistantToLeftTopRectangles, mostDistantToRightTopRectangles,
mostDistantToLeftBottomRectangles, mostDistantToRightBottomRectangles);

return Math.Sqrt(Math.Pow(mostDistant, 2) +
Math.Pow(mostDistant, 2));
}

private static double GetAreaOfCircle(double radius) => Math.PI * Math.Pow(radius, 2);

private static int GetAreaOfRectangles(IEnumerable<Rectangle> rectangles) =>
rectangles.Sum(r => r.Width * r.Height);

private static double GetDensityCoefficient(
double areaOfRectangles,
double areaOfCircle,
double averageDistance,
double radius) =>
areaOfRectangles / areaOfCircle * (1 - averageDistance / radius);

Choose a reason for hiding this comment

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

Не понимаю, что здесь дает второй множитель?

Choose a reason for hiding this comment

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

А первый множитель расчитан неправильно. Перепроверь пожалуйста.
Вообще я посмотрел. У тебя плотность на твоих тестовых данных выше 75% везде. Это хороший результат 🔥


private static int GetMaxX(IEnumerable<Rectangle> rectangles) => rectangles.Max(r => r.Location.X);

private static int GetMaxY(IEnumerable<Rectangle> rectangles) => rectangles.Max(r => r.Location.Y);

private static int GetMinX(IEnumerable<Rectangle> rectangles) => rectangles.Min(r => r.Location.X);

private static int GetMinY(IEnumerable<Rectangle> rectangles) => rectangles.Min(r => r.Location.Y);

private static int Max(int p1, int p2, int p3, int p4) => Math.Max(Math.Max(p1, p2), Math.Max(p3, p4));
}
60 changes: 60 additions & 0 deletions cs/TagsCloudVisualization.Tests/ImageSaverTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Drawing;
using System.Runtime.InteropServices;
using FluentAssertions;
using NUnit.Framework;
using TagsCloudVisualization.CloudLayouter;
using TagsCloudVisualization.Draw;
using TagsCloudVisualization.Extension;
using TagsCloudVisualization.RectangleGenerator;
using TagsCloudVisualization.Saver;

namespace TagsCloudVisualizationTests;

[TestFixture]
public class ImageSaverTest
{
private Point center;
private CircularCloudLayouter layouter;
private IEnumerable<Rectangle> rectangles;
private RectangleDraftsman drawer;

[SetUp]
public void SetUp()
{
center = new Point(0, 0);
layouter = new CircularCloudLayouter(center);
rectangles = RectangleGenerator.GenerateRandomRectangles(10);
layouter.PutRectangles(rectangles);
drawer = new RectangleDraftsman(1500, 1500);
}

[TestCase(null)]
public void CreateImage_OnInvalidParameters_ThrowsArgumentException(string filename)
{
drawer.CreateImage(layouter.Rectangles);
var action = () => drawer.SaveImageToFile(filename);
action.Should().Throw<ArgumentException>();
}

[TestCase("12\\")]
[TestCase("@#$\\")]
public void CreateImage_OnInvalidParameters_ThrowsDirectoryNotFoundException(string filename)
{
drawer.CreateImage(layouter.Rectangles);
var action = () => drawer.SaveImageToFile(filename);
action.Should().Throw<DirectoryNotFoundException>();
}

[TestCase("abc|123")]
[TestCase("123|abc")]
[TestCase("123\n")]
[TestCase("123\r")]
[TestCase("\\")]
[TestCase("")]
public void CreateImage_OnInvalidParameters_ThrowsExternalException(string filename)
{
drawer.CreateImage(layouter.Rectangles);
var action = () => drawer.SaveImageToFile(filename);
action.Should().Throw<ExternalException>();
}
}
51 changes: 51 additions & 0 deletions cs/TagsCloudVisualization.Tests/PointExtensionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Drawing;
using NUnit.Framework;
using FluentAssertions;
using TagsCloudVisualization.Extension;

namespace TagsCloudVisualizationTests;

[TestFixture]
public class PointExtensionTest
{
[TestCase(0, 0, 0, 0, 0, 0, Description = "Subtract of zero points")]
[TestCase(1, 1, 1, 1, 0, 0, Description = "Subtract of positive points")]
[TestCase(-1, -1, -1, -1, 0, 0, Description = "Subtract of negative points")]
public void Subtract_ReturnsDifferenceOfPointCoordinates(
int xPoint1,
int yPoint1,
int xPoint2,
int yPoint2,
int xResult,
int yResult)
{
var point1 = new Point(xPoint1, yPoint1);
var point2 = new Point(xPoint2, yPoint2);
var result = point1.Subtract(point2);
result.X.Should().Be(xResult);
result.Y.Should().Be(yResult);
}

[Test]
[TestCaseSource(nameof(PointerSumTestCases))]
public Point Add_ReturnsSumOfPointCoordinates(
Point point1,
Point point2,
Point correctResult)
{
return point1.Add(point2);
}

private static IEnumerable<TestCaseData> PointerSumTestCases()

Choose a reason for hiding this comment

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

Потренировался - молодец)

{
yield return new TestCaseData(new Point(0, 0), new Point(0, 0), new Point(0, 0))
.Returns(Point.Empty)
.SetName("Addition of zero points");
yield return new TestCaseData(new Point(1, 1), new Point(1, 1), new Point(2, 2))
.Returns(new Point(2, 2))
.SetName("Addition of positive points");
yield return new TestCaseData(new Point(-1, -1), new Point(-1, -1), new Point(-2, -2))
.Returns(new Point(-2, -2))
.SetName("Addition of negative points");
}
}
Loading