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

Ватлин Алексей #242

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Binary file added cs/Images/1000_0,1_0,1_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/Images/1000_0,1_20_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/Images/1000_0,1_5_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/Images/1000_1_0,1_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/Images/1000_1_1_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/Images/1000_20_0,001_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/Images/1000_20_0,1_TagCloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions cs/TagsCloudVisualization/CloudLayouter/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Drawing;
using TagsCloudVisualization.PointsGenerators;

namespace TagsCloudVisualization.CloudLayouter;

public class CircularCloudLayouter : ICircularCloudLayouter
{
private Point center;

This comment was marked as resolved.

private List<Rectangle> rectangles = [];

This comment was marked as resolved.

private SpiralPointsGenerator spiral;
private double step = Constans.LayoutStep;
private double angleOffset = Constans.LayoutAngleOffset;

public CircularCloudLayouter(Point center)
{
this.center = center;
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

This comment was marked as resolved.

rectangle = GetNextRectangle(rectangleSize);
rectangles.Add(rectangle);
return rectangle;
}

private Rectangle GetNextRectangle(Size rectagleSize)

This comment was marked as resolved.

{
var rectanglePosition = spiral.GetNextPointPosition();
var centerOfRectangle = CreateRectangleWithCenter(rectanglePosition, rectagleSize);

This comment was marked as resolved.

return centerOfRectangle;
}

private Rectangle CreateRectangleWithCenter(Point center, Size rectangleSize)

This comment was marked as resolved.

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

namespace TagsCloudVisualization.CloudLayouter;

public static class CloudGenerator
{
public static CircularCloudLayouter GenerateCloud(int rectanglesNumber = Constans.RectanglesNumber)

This comment was marked as resolved.

{
var center = new Point(Constans.ImageWidth / 2, Constans.ImageHeight / 2);

This comment was marked as resolved.

var cloudLayouter = new CircularCloudLayouter(center);
var random = new Random();
var rectangles = new Rectangle[rectanglesNumber];
rectangles = rectangles

This comment was marked as resolved.

.Select(x => cloudLayouter.PutNextRectangle(new Size(
random.Next(Constans.MinRectangleSize, Constans.MaxRectangleSize),
random.Next(Constans.MinRectangleSize, Constans.MaxRectangleSize))))
.ToArray();
return cloudLayouter;
}

This comment was marked as resolved.

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

namespace TagsCloudVisualization.CloudLayouter;

public interface ICircularCloudLayouter
{
Rectangle PutNextRectangle(Size rectangleSize);
}
14 changes: 14 additions & 0 deletions cs/TagsCloudVisualization/Constans.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace TagsCloudVisualization;

public static class Constans
{
public const int ImageWidth = 1500;
public const int ImageHeight = 1500;
public const int RectanglesNumber = 1000;
public const int MinRectangleSize = 10;
public const int MaxRectangleSize = 50;
public const double LayoutStep = 0.1;
public const double LayoutAngleOffset = 0.1;
public const string ImagesDirectory = "images";
}

This comment was marked as resolved.


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

namespace TagsCloudVisualization.PointsGenerators;

public class SpiralPointsGenerator
Alex-Vay marked this conversation as resolved.
Show resolved Hide resolved
{
private double angleOffset;
private readonly Point center;
private readonly double step;
private double angle = 0;

Choose a reason for hiding this comment

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

Лучше переименовать на currentAngle


public SpiralPointsGenerator(Point center, double step, double angleOffset)
{
this.center = center;
this.step = step;
this.angleOffset = angleOffset;
}

public Point GetNextPointPosition()
{
var radius = step * angle;
var x = (int)(center.X + radius * Math.Cos(angle));
var y = (int)(center.Y + radius * Math.Sin(angle));
angle += angleOffset;
return new(x, y);
}
}
16 changes: 16 additions & 0 deletions cs/TagsCloudVisualization/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Drawing;
using TagsCloudVisualization.Visualizers;
using TagsCloudVisualization.CloudLayouter;

namespace TagsCloudVisualization;

public static class Program
{
public static void Main()
{
var cloudLayouter = CloudGenerator.GenerateCloud();
var rectangles = cloudLayouter.GetRectangles();
var visualizer = new SimpleCloudVisualizer();
visualizer.CreateBitmap(rectangles, new Size(Constans.ImageWidth, Constans.ImageHeight));
}
}
15 changes: 15 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.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>
<BaseOutputPath></BaseOutputPath>
</PropertyGroup>

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

</Project>
39 changes: 39 additions & 0 deletions cs/TagsCloudVisualization/Visualizers/SimpleCloudVisualizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Drawing;
using System.Drawing.Imaging;

namespace TagsCloudVisualization.Visualizers;

public class SimpleCloudVisualizer
{
public void CreateBitmap(
IEnumerable<Rectangle> rectangles,
Size bitmapSize,
string directory = Constans.ImagesDirectory,
string currentPath = null

This comment was marked as resolved.

)
{
var bitmap = new Bitmap(bitmapSize.Width, bitmapSize.Height);
var graphics = Graphics.FromImage(bitmap);

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.

using graphics = ...

graphics.Clear(Color.White);
foreach (var rectangle in rectangles)
{
var pen = new Pen(GetRandomColor());
graphics.DrawRectangle(pen, rectangle);
}
Directory.CreateDirectory(directory);
var path = currentPath == null ? GetPathToImages() : currentPath;
bitmap.Save(path, ImageFormat.Jpeg);
}

private Color GetRandomColor()

This comment was marked as resolved.

{
var random = new Random();
return Color.FromArgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));
}

private static string GetPathToImages()
{
var filename = $"{Constans.RectanglesNumber}_{Constans.LayoutStep}_{Constans.LayoutAngleOffset}_TagCloud.jpg";
return Path.Combine(Constans.ImagesDirectory, filename);
}
}
146 changes: 146 additions & 0 deletions cs/TagsCloudVisualizationTests/CircularCloudLayouterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using NUnit.Framework.Interfaces;
using FluentAssertions;
using System.Drawing;
using TagsCloudVisualization.CloudLayouter;
using TagsCloudVisualization.Visualizers;
using TagsCloudVisualization;

namespace TagsCloudVisualizationTests.CircularCloudLayouterTest;
Alex-Vay marked this conversation as resolved.
Show resolved Hide resolved

[TestFixture]
public class CircularCloudLayouterTests
{
private List<Rectangle> rectanglesInTest;
Alex-Vay marked this conversation as resolved.
Show resolved Hide resolved

[TearDown]
public void TearDown()
{
if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed)
return;
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);
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)

This comment was marked as resolved.

{
var layouter = new CircularCloudLayouter(new(0, 0));
var size = new Size(width, height);

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

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

[Test]
public void FirstRectang_ShouldBeInCenter()

This comment was marked as resolved.

{
var layouter = new CircularCloudLayouter(new(0, 0));
var rectangleSize = new Size(10, 10);

var actualRectangle = layouter.PutNextRectangle(rectangleSize);
var expectedRectangle = new Rectangle(

This comment was marked as resolved.

-rectangleSize.Width / 2,
-rectangleSize.Height / 2,
rectangleSize.Width,
rectangleSize.Height
);
rectanglesInTest = layouter.GetRectangles();

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

[Test]
[Repeat(10)]
public void Rectangles_ShouldNotHaveIntersects()

Choose a reason for hiding this comment

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

Тест-метод должен говорить, какой метод мы тестируем

{
var layouter = CloudGenerator.GenerateCloud(100);
rectanglesInTest = layouter.GetRectangles();

var expectedResult = AreRectanglesHaveIntersects(rectanglesInTest);
Alex-Vay marked this conversation as resolved.
Show resolved Hide resolved

expectedResult.Should().BeFalse();
}

[Test]
[Repeat(10)]
public void AllRectanglesCenter_ShoulBeLikeInitCenter()

This comment was marked as resolved.

{
var center = new Point(Constans.ImageWidth / 2, Constans.ImageHeight / 2);
var treshold = Constans.MaxRectangleSize / 2;
var layouter = CloudGenerator.GenerateCloud(100);
rectanglesInTest = layouter.GetRectangles();

var actualCenter = GetCenterOfAllRectangles(rectanglesInTest);

This comment was marked as resolved.


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()

This comment was marked as resolved.

{
var expectedDensity = 0.5;
var center = new Point(Constans.ImageWidth / 2, Constans.ImageHeight / 2);
var layouter = CloudGenerator.GenerateCloud(100);
rectanglesInTest = layouter.GetRectangles();

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

This comment was marked as resolved.

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

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

private Point GetCenterOfAllRectangles(List<Rectangle> rectangles)
{
var top = rectangles.Max(r => r.Top);
var right = rectangles.Max(r => r.Right);
var bottom = rectangles.Min(r => r.Bottom);
var left = rectangles.Min(r => r.Left);
var x = left + (right - left) / 2;
var y = bottom + (top - bottom) / 2;
return new(x, y);
}

private double GetMaxDistanceBetweenRectangleAndCenter(List<Rectangle> rectangles)
{
var center = GetCenterOfAllRectangles(rectangles);
double maxDistance = -1;
foreach (var rectangle in rectangles)
{
var corners = new Point[4]
{
new(rectangle.Top, rectangle.Left),
new(rectangle.Bottom, rectangle.Left),
new(rectangle.Top, rectangle.Right),
new(rectangle.Bottom, rectangle.Right)
};
var distance = corners.Max(p => GetDistanceBetweenPoints(p, center));
maxDistance = Math.Max(maxDistance, distance);
}
return maxDistance;
}



private bool AreRectanglesHaveIntersects(List<Rectangle> rectangles)
{
for (var i = 0; i < rectangles.Count; i++)
for (var j = i + 1; j < rectangles.Count; j++)
if (rectangles[i].IntersectsWith(rectangles[j]))
return true;
return false;
}

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

This comment was marked as resolved.

29 changes: 29 additions & 0 deletions cs/TagsCloudVisualizationTests/TagsCloudVisualizationTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

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

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

</Project>
Loading