-
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
Ватлин Алексей #242
base: master
Are you sure you want to change the base?
Ватлин Алексей #242
Changes from 3 commits
4480f67
9a47b32
8aa0286
cd62acd
050cfd2
ea634c8
1b8b082
cfe7eba
1682c94
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,46 @@ | ||
using System.Drawing; | ||
using TagsCloudVisualization.PointsGenerators; | ||
|
||
namespace TagsCloudVisualization.CloudLayouter; | ||
|
||
public class CircularCloudLayouter : ICircularCloudLayouter | ||
{ | ||
private Point center; | ||
private List<Rectangle> rectangles = []; | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
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.
Sorry, something went wrong. |
||
rectangle = GetNextRectangle(rectangleSize); | ||
rectangles.Add(rectangle); | ||
return rectangle; | ||
} | ||
|
||
private Rectangle GetNextRectangle(Size rectagleSize) | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
{ | ||
var rectanglePosition = spiral.GetNextPointPosition(); | ||
var centerOfRectangle = CreateRectangleWithCenter(rectanglePosition, rectagleSize); | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
return centerOfRectangle; | ||
} | ||
|
||
private Rectangle CreateRectangleWithCenter(Point center, Size rectangleSize) | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
{ | ||
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; | ||
} |
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.
Sorry, something went wrong. |
||
{ | ||
var center = new Point(Constans.ImageWidth / 2, Constans.ImageHeight / 2); | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
var cloudLayouter = new CircularCloudLayouter(center); | ||
var random = new Random(); | ||
var rectangles = new Rectangle[rectanglesNumber]; | ||
rectangles = rectangles | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
.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.
Sorry, something went wrong. |
||
} |
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); | ||
} |
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.
Sorry, something went wrong. |
||
|
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; | ||
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. Лучше переименовать на |
||
|
||
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); | ||
} | ||
} |
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)); | ||
} | ||
} |
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> |
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.
Sorry, something went wrong. |
||
) | ||
{ | ||
var bitmap = new Bitmap(bitmapSize.Width, bitmapSize.Height); | ||
var graphics = Graphics.FromImage(bitmap); | ||
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. Не диспоузим этот объект - потеря памяти 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. 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.
Sorry, something went wrong. |
||
{ | ||
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); | ||
} | ||
} |
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.
Sorry, something went wrong. |
||
{ | ||
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.
Sorry, something went wrong. |
||
{ | ||
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.
Sorry, something went wrong. |
||
-rectangleSize.Width / 2, | ||
-rectangleSize.Height / 2, | ||
rectangleSize.Width, | ||
rectangleSize.Height | ||
); | ||
rectanglesInTest = layouter.GetRectangles(); | ||
|
||
actualRectangle.Should().BeEquivalentTo(expectedRectangle); | ||
} | ||
|
||
[Test] | ||
[Repeat(10)] | ||
public void Rectangles_ShouldNotHaveIntersects() | ||
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. Тест-метод должен говорить, какой метод мы тестируем |
||
{ | ||
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.
Sorry, something went wrong. |
||
{ | ||
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.
Sorry, something went wrong. |
||
|
||
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.
Sorry, something went wrong. |
||
{ | ||
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.
Sorry, something went wrong. |
||
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.
Sorry, something went wrong. |
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> |
This comment was marked as resolved.
Sorry, something went wrong.