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

Косторной Дмитрий #246

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 43 additions & 0 deletions cs/TagsCloudTests/ArchimedeanSpiralPointGenerator_Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using FluentAssertions;
using NUnit.Framework;
using SixLabors.ImageSharp;

namespace TagsCloudTests;

public class ArchimedeanSpiralPointGenerator_Should : CircularCloudLayouterTestsBase
{
[Test]
public void GetNextPoint_ShouldReturnFirstPointAtCenter()
{
var point = Generator.GetNextPoint();
point.Should().Be(Center);
}

[Test]
public void GetNextPoint_ShouldReturnDifferentPoints()
{
var point1 = Generator.GetNextPoint();
var point2 = Generator.GetNextPoint();
point1.Should().NotBe(point2);
}

[Test]
public void GetNextPoint_ShouldGeneratePointsInSpiralPattern()
{
var previousPoint = Generator.GetNextPoint();
var previousDistance = Distance(Center, previousPoint);

for (int i = 0; i < 10; i++)
{
var currentPoint = Generator.GetNextPoint();
var currentDistance = Distance(Center, currentPoint);
currentDistance.Should().BeGreaterThan(previousDistance);
previousDistance = currentDistance;
}
}

private float Distance(PointF p1, PointF p2)
{
return (float)Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
}
}
53 changes: 53 additions & 0 deletions cs/TagsCloudTests/CircularCloudLayouterTestsBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using NUnit.Framework;
using SixLabors.ImageSharp;
using TagsCloudVisualization.CloudLayouter;

namespace TagsCloudTests;

[TestFixture]
public abstract class CircularCloudLayouterTestsBase
{
protected CircularCloudLayouter Layouter;
protected CloudVisualizer Visualizer;
protected ArchimedeanSpiralPointGenerator Generator;
protected SizeF[] RectangleSizes;
protected PointF Center;
protected int MaxDistanceToCenter;
protected string ImagesDirectory;

[SetUp]
public virtual void SetUp()
{
Center = new PointF(600, 450);
Layouter = new CircularCloudLayouter(Center);
Visualizer = new CloudVisualizer();
RectangleSizes =
[
new SizeF(70, 100),
new SizeF(60, 60),
new SizeF(90, 30),
new SizeF(75, 115),
new SizeF(100, 100)
];
Generator = new ArchimedeanSpiralPointGenerator(Center);
MaxDistanceToCenter = 120;
var projectDirectory = Directory.GetParent(Environment.CurrentDirectory)?.Parent?.Parent?.FullName
?? throw new InvalidOperationException("Не удалось определить директорию проекта.");
ImagesDirectory = Path.Combine(projectDirectory, "Images");
if (!Directory.Exists(ImagesDirectory))
{
Directory.CreateDirectory(ImagesDirectory);
}
}

[TearDown]
public virtual void TearDown()
{
if (TestContext.CurrentContext.Result.Outcome.Status != NUnit.Framework.Interfaces.TestStatus.Failed)
return;
var testName = TestContext.CurrentContext.Test.Name;
var filePath = Path.Combine(ImagesDirectory, $"{testName}_failed.png");
Visualizer.SaveVisualization(Layouter.Rectangles, filePath);
Console.WriteLine($"Tag cloud visualization saved to file {filePath}");
}
}
109 changes: 109 additions & 0 deletions cs/TagsCloudTests/CircularCloudLayouter_Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using FluentAssertions;
using NUnit.Framework;
using SixLabors.ImageSharp;

namespace TagsCloudTests;

[TestFixture]
public class CircularCloudLayouter_Should : CircularCloudLayouterTestsBase
{
[Test]
public void CircularCloud_ShouldBeEmpty_WhenCreated()
{
Layouter.Rectangles.Should().BeEmpty();
}

[TestCase(0, 10)]
[TestCase(10, 0)]
[TestCase(-1, 10)]
[TestCase(10, -1)]
[TestCase(0, 0)]
[TestCase(-10, -10)]
public void CircularCloud_ShouldThrowArgumentException_WhenInvalidRectangleSize(int width, int height)
{
var func = () => Layouter.PutNextRectangle(new SizeF(width, height));
func.Should().Throw<ArgumentException>();
}

[Test]
public void PutNextRectangle_ShouldReturnRectangleWithSameSize()
{
var rectangleSize = new SizeF(20, 30);
var rectangle = Layouter.PutNextRectangle(rectangleSize);

rectangle.Size.Should().BeEquivalentTo(rectangleSize);
}

[Test]
public void PutNextRectangle_ShouldPlaceRectangleInCenter_WhenFirstRectangle()
{
var rectangleSize = new SizeF(10, 10);
var expectedLocation = new PointF(Center.X - rectangleSize.Width / 2, Center.Y - rectangleSize.Height / 2);
var rectangle = Layouter.PutNextRectangle(rectangleSize);

rectangle.Location.Should().BeEquivalentTo(expectedLocation);
}

[Test]
public void PutNextRectangle_ShouldNotIntersectWithPreviousRectangles()
{
foreach (var size in RectangleSizes)
{
Layouter.PutNextRectangle(size);
}
VerifyRectanglesDontIntersect(Layouter.Rectangles.ToList());
}

[Test]
public void PutNextRectangle_ShouldNotIntersect_WhenPlacingIdenticalRectangles()
{
var rectangleSize = new SizeF(10, 10);
var numberOfRectangles = 10;

for (int i = 0; i < numberOfRectangles; i++)
{
Layouter.PutNextRectangle(rectangleSize);
}
VerifyRectanglesDontIntersect(Layouter.Rectangles.ToList());
}

[Test]
public void CircularCloud_ShouldHaveAllPlacedRectangles()
{
foreach (var size in RectangleSizes)
{
Layouter.PutNextRectangle(size);
}

Layouter.Rectangles.Should().HaveCount(RectangleSizes.Length);
}

[Test]
public void CircularCloudRectangles_ShouldBeCloseToCenter()
{
foreach (var size in RectangleSizes)
{
Layouter.PutNextRectangle(size);
}

var rectangles = Layouter.Rectangles.ToList();

foreach (var rectangle in rectangles)
{
var distanceToCenter = Math.Sqrt(Math.Pow(rectangle.X + rectangle.Width / 2 - Center.X, 2) +
Math.Pow(rectangle.Y + rectangle.Height / 2 - Center.Y, 2));
distanceToCenter.Should().BeLessThan(MaxDistanceToCenter);
}
}

private void VerifyRectanglesDontIntersect(List<RectangleF> rectangles)
{
for (int i = 0; i < rectangles.Count; i++)
{
for (int j = i + 1; j < rectangles.Count; j++)
{
rectangles[i].IntersectsWith(rectangles[j]).Should().BeFalse();
}
}
}
}
50 changes: 50 additions & 0 deletions cs/TagsCloudTests/CloudVisualizer_Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using FluentAssertions;
using NUnit.Framework;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

namespace TagsCloudTests;

public class CloudVisualizer_Should : CircularCloudLayouterTestsBase
{
[Test]
public void SaveVisualization_ShouldCreateImageWithDefaultSize_WhenSmallRectangles()
{
Layouter.PutNextRectangle(new SizeF(10, 10));
var filePath = Path.Combine(ImagesDirectory, "test_visualization.png");
Visualizer.SaveVisualization(Layouter.Rectangles, filePath);

using (var image = Image.Load<Rgba32>(filePath))
{
image.Width.Should().Be(1200);
image.Height.Should().Be(900);
}
File.Delete(filePath);
}

[Test]
public void SaveVisualization_ShouldCreateImageWithBiggerSize_WhenManyRectangles()
{
for(int i = 0; i < 13; i++)
Layouter.PutNextRectangle(new SizeF(300, 300));

var filePath = Path.Combine(ImagesDirectory, "test_visualization.png");
Visualizer.SaveVisualization(Layouter.Rectangles, filePath);

using (var image = Image.Load<Rgba32>(filePath))
{
image.Width.Should().BeGreaterThan(1200);
image.Height.Should().BeGreaterThan(900);
}
File.Delete(filePath);
}

[Test]
public void SaveVisualization_ShouldThrowDirectoryNotFoundException_WhenInvalidPath()
{
var invalidPath = @"M:\NonExistingDirectory\test_visualisation.png";
var func = () => Visualizer.SaveVisualization(Layouter.Rectangles, invalidPath);

func.Should().Throw<DirectoryNotFoundException>();
}
}
51 changes: 51 additions & 0 deletions cs/TagsCloudTests/RectangleExtensions_Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using FluentAssertions;
using NUnit.Framework;
using SixLabors.ImageSharp;
using TagsCloudVisualization.CloudLayouter;

namespace TagsCloudTests;

[TestFixture]
public class RectangleExtensions_Should : CircularCloudLayouterTestsBase
{
[Test]
public void IntersectsWithAny_ShouldReturnTrue_WhenRectangleIntersectsWithAnyInCollection()
{
var rectangleToCheck = new RectangleF(50, 50, 100, 100);
var existingRectangles = new List<RectangleF>
{
new RectangleF(30, 30, 50, 50),
new RectangleF(200, 200, 50, 50)
};

var result = rectangleToCheck.IntersectsWithAny(existingRectangles);

result.Should().BeTrue();
}

[Test]
public void IntersectsWithAny_ShouldReturnFalse_WhenNoIntersectionWithAnyInCollection()
{
var rectangleToCheck = new RectangleF(50, 50, 100, 100);
var existingRectangles = new List<RectangleF>
{
new RectangleF(200, 200, 50, 50),
new RectangleF(300, 300, 50, 50)
};

var result = rectangleToCheck.IntersectsWithAny(existingRectangles);

result.Should().BeFalse();
}

[Test]
public void IntersectsWithAny_ShouldReturnFalse_WhenCollectionIsEmpty()
{
var rectangleToCheck = new RectangleF(50, 50, 100, 100);
var existingRectangles = new List<RectangleF>();

var result = rectangleToCheck.IntersectsWithAny(existingRectangles);

result.Should().BeFalse();
}
}
22 changes: 22 additions & 0 deletions cs/TagsCloudTests/TagsCloudTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Library</OutputType>
<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" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0-beta.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
</ItemGroup>

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

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using SixLabors.ImageSharp;

namespace TagsCloudVisualization.CloudLayouter;

public class ArchimedeanSpiralPointGenerator
{
private readonly PointF center;
private readonly double spiralStep;
private readonly double distanceBetweenTurns;
private double angle;

public ArchimedeanSpiralPointGenerator(PointF center, double spiralStep = 0.01, double distanceBetweenTurns = 0.01)
{
this.center = center;
this.spiralStep = spiralStep;
this.distanceBetweenTurns = distanceBetweenTurns;
angle = 0;
}

public PointF GetNextPoint()
{
var radius = distanceBetweenTurns * angle;
var x = (float)(center.X + radius * Math.Cos(angle));
var y = (float)(center.Y + radius * Math.Sin(angle));
angle += spiralStep;
return new PointF(x, y);
}
}
Loading