-
Notifications
You must be signed in to change notification settings - Fork 293
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
Sibogatov Rinat #221
base: master
Are you sure you want to change the base?
Sibogatov Rinat #221
Changes from 2 commits
042dc0b
2713ff7
78c4639
91b862d
2732c1f
646e85b
6344969
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,62 @@ | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVizualization; | ||
|
||
public class CircularCloudLayouter : ICircularCloudLayouter | ||
{ | ||
private readonly Point center; | ||
private readonly List<Rectangle> rectangles; | ||
private readonly Spiral spiral; | ||
|
||
public CircularCloudLayouter(Point center) | ||
{ | ||
this.center = center; | ||
rectangles = new(); | ||
spiral = new(center, 0.02, 0.01); | ||
} | ||
|
||
public Point CloudCenter => center; | ||
public IList<Rectangle> Rectangles => rectangles; | ||
|
||
public Rectangle PutNextRectangle(Size rectangleSize) | ||
{ | ||
ValidateRectangleSize(rectangleSize); | ||
|
||
var currentRectangle = CreateNewRectangle(rectangleSize); | ||
rectangles.Add(currentRectangle); | ||
|
||
return currentRectangle; | ||
} | ||
|
||
private void ValidateRectangleSize(Size size) | ||
{ | ||
if (size.Width <= 0 || size.Height <= 0) | ||
{ | ||
throw new ArgumentException("Rectangle width and height must be positive"); | ||
} | ||
|
||
} | ||
|
||
private Rectangle CreateNewRectangle(Size rectangleSize) | ||
{ | ||
Rectangle rectangle; | ||
do | ||
{ | ||
var pointOnSpiral = spiral.GetNextPointOnSpiral(); | ||
var rectangleLocation = GetUpperLeftCorner(pointOnSpiral, rectangleSize); | ||
rectangle = new Rectangle(rectangleLocation, rectangleSize); | ||
} while (IntersectsExisting(rectangle)); | ||
|
||
return rectangle; | ||
} | ||
|
||
private bool IntersectsExisting(Rectangle rectangle) | ||
{ | ||
return rectangles.Any(rect => rect.IntersectsWith(rectangle)); | ||
} | ||
|
||
private Point GetUpperLeftCorner(Point centerPoint, Size size) | ||
{ | ||
return new Point(centerPoint.X - size.Width / 2, centerPoint.Y - size.Height / 2); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVizualization; | ||
|
||
public interface ICircularCloudLayouter | ||
{ | ||
public Point CloudCenter { get; } | ||
public IList<Rectangle> Rectangles { get; } | ||
Rectangle PutNextRectangle(Size rectangleSize); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVizualization; | ||
|
||
public class Program | ||
{ | ||
public const int ImageWidth = 720; | ||
public const int ImageHeight = 720; | ||
public const int CountRectangles = 10000; | ||
public const string PathToImages = @"..\..\..\Images"; | ||
|
||
public static void Main(string[] args) | ||
{ | ||
var layouter = CreateLayouter(); | ||
GenerateRandomRectangles(layouter); | ||
|
||
var image = VisualizeLayout(layouter); | ||
|
||
SaveImage(image); | ||
} | ||
|
||
private static CircularCloudLayouter CreateLayouter() | ||
{ | ||
var center = new Point(ImageWidth / 2, ImageHeight / 2); | ||
return new CircularCloudLayouter(center); | ||
} | ||
|
||
private static void GenerateRandomRectangles(CircularCloudLayouter layouter) | ||
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 random = new Random(); | ||
for (int i = 0; i < CountRectangles; i++) | ||
{ | ||
var rectangleSize = new Size(random.Next(5, 20), random.Next(5, 20)); | ||
layouter.PutNextRectangle(rectangleSize); | ||
} | ||
} | ||
|
||
private static Bitmap VisualizeLayout(CircularCloudLayouter layouter) | ||
{ | ||
return Visualizer.VisualizeRectangles(layouter.Rectangles, ImageWidth, ImageHeight); | ||
} | ||
|
||
private static void SaveImage(Bitmap image) | ||
{ | ||
var fileName = $"{CountRectangles}_Rectangles.png"; | ||
Visualizer.SaveBitmap(image, fileName, PathToImages); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# TagsCouldVizualization | ||
|
||
![8 Rectangles](Images/8_Rectangles.png) | ||
|
||
![128 Rectangles](Images/128_Rectangles.png) | ||
|
||
![500 Rectangles](Images/500_Rectangles.png) | ||
|
||
![1000 Rectangles](Images/1000_Rectangles.png) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="System.Drawing.Common" Version="8.0.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="Images\" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVizualization; | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
||
public class Spiral | ||
{ | ||
private readonly Point center; | ||
private readonly double angleStep; | ||
private readonly double radiusStep; | ||
private double radius; | ||
private double angle; | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
||
public Spiral(Point center, double angleStep, double radiusStep) | ||
{ | ||
if (angleStep <= 0 || radiusStep <= 0) | ||
{ | ||
throw new ArgumentException("Step values should be positive."); | ||
} | ||
this.center = center; | ||
this.angleStep = angleStep; | ||
this.radiusStep = radiusStep; | ||
radius = 0; | ||
angle = 0; | ||
} | ||
public Point GetNextPointOnSpiral() | ||
{ | ||
var currentPoint = ConvertFromPolarToCartesian(); | ||
angle += angleStep; | ||
radius += radiusStep; | ||
return currentPoint; | ||
} | ||
|
||
private Point ConvertFromPolarToCartesian() | ||
{ | ||
var x = (int)Math.Round(center.X + Math.Cos(angle) * radius); | ||
var y = (int)Math.Round(center.Y + Math.Sin(angle) * radius); | ||
return new Point(x, y); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVizualization; | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
||
public static class Visualizer | ||
{ | ||
public static Bitmap VisualizeRectangles(IList<Rectangle> rectangles, int bitmapWidth, int bitmapHeight) | ||
{ | ||
var bitmap = new Bitmap(bitmapWidth, bitmapHeight); | ||
using var graphics = Graphics.FromImage(bitmap); | ||
|
||
DrawRectangles(rectangles, graphics); | ||
|
||
return bitmap; | ||
} | ||
|
||
private static void DrawRectangles(IEnumerable<Rectangle> rectangles, Graphics graphics) | ||
{ | ||
var pen = new Pen(Color.Green); | ||
foreach (var rect in rectangles) | ||
{ | ||
graphics.DrawRectangle(pen, rect); | ||
} | ||
} | ||
|
||
public static void SaveBitmap(Bitmap bitmap, string fileName, string pathToDirectory) | ||
{ | ||
EnsureDirectoryExists(pathToDirectory); | ||
|
||
var safeFileName = GetSafeFileName(fileName); | ||
bitmap.Save(Path.Combine(pathToDirectory, safeFileName), System.Drawing.Imaging.ImageFormat.Png); | ||
} | ||
|
||
private static void EnsureDirectoryExists(string directory) | ||
{ | ||
if (!Directory.Exists(directory)) | ||
{ | ||
Directory.CreateDirectory(directory); | ||
} | ||
} | ||
|
||
private static string GetSafeFileName(string fileName) | ||
{ | ||
var invalidChars = Path.GetInvalidFileNameChars(); | ||
return string.Concat(fileName.Split(invalidChars, StringSplitOptions.RemoveEmptyEntries)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
using System.Drawing; | ||
using TagsCloudVizualization; | ||
using FluentAssertions; | ||
|
||
namespace TagsCloudVizualizationTests; | ||
|
||
public class CircularCloudLayouterTests | ||
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong. |
||
{ | ||
private CircularCloudLayouter sut; | ||
private Point center = new(720, 720); | ||
|
||
[SetUp] | ||
public void Setup() | ||
{ | ||
sut = new CircularCloudLayouter(center); | ||
} | ||
|
||
[Test] | ||
public void Constructor_ShouldNotThrow() | ||
{ | ||
Assert.DoesNotThrow(() => new CircularCloudLayouter(center)); | ||
} | ||
|
||
[TestCase(-4, 16, TestName = "PutNextRectangle_WidthNotPositive_ThrowsArgumentException")] | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
[TestCase(77, -8, TestName = "PutNextRectangle_HeightNotPositive_ThrowsArgumentException")] | ||
public void PutNextRectangle_InvalidSize_ThrowsArgumentException(int rectangleWidth, int rectangleHeight) | ||
{ | ||
Assert.Throws<ArgumentException>(() => sut.PutNextRectangle(new Size(rectangleWidth, rectangleHeight))); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_ShouldReturnCorrectRectangleSize() | ||
{ | ||
var rectangle = sut.PutNextRectangle(new Size(8, 8)); | ||
|
||
rectangle.Size.Should().Be(new Size(8, 8)); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_FirstRectangle_ShouldHaveCenterAtLayoutCenter() | ||
{ | ||
var rectangle = sut.PutNextRectangle(new Size(8, 8)); | ||
|
||
var expectedRectangleCenter = new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2); | ||
expectedRectangleCenter.Should().Be(center); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_TwoRectangles_SecondRectangleCenterShouldNotBeAtLayoutCenter() | ||
{ | ||
sut.PutNextRectangle(new Size(8, 8)); | ||
|
||
var secondRectangle = sut.PutNextRectangle(new Size(6, 6)); | ||
|
||
var expectedRectangleCenter = new Point( | ||
secondRectangle.Left + secondRectangle.Width / 2, | ||
secondRectangle.Top + secondRectangle.Height / 2); | ||
|
||
expectedRectangleCenter.Should().NotBe(center); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_TwoRectangles_ShouldNotIntersect() | ||
{ | ||
var firstRectangle = sut.PutNextRectangle(new Size(8, 8)); | ||
|
||
var secondRectangle = sut.PutNextRectangle(new Size(77, 77)); | ||
|
||
secondRectangle.IntersectsWith(firstRectangle).Should().BeFalse(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
global using NUnit.Framework; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System.Drawing; | ||
using TagsCloudVizualization; | ||
using FluentAssertions; | ||
|
||
namespace TagsCloudVizualizationTests; | ||
|
||
public class SpiralTests | ||
{ | ||
private Point center = new(250, 250); | ||
|
||
[Test] | ||
public void Constructor_ShouldNotThrow_WithValidArguments() | ||
{ | ||
Action constructorAction = () => new Spiral(center, 0.1, 0.2); | ||
constructorAction.Should().NotThrow(); | ||
} | ||
|
||
[TestCase(0, 0, TestName = "Constructor_ZeroStep_ThrowsArgumentException")] | ||
[TestCase(-0.1, 0.2, TestName = "Constructor_NegativeAngleStep_ThrowsArgumentException")] | ||
[TestCase(0.1, -0.2, TestName = "Constructor_NegativeRadiusStep_ThrowsArgumentException")] | ||
public void Constructor_InvalidStepValues_ThrowsArgumentException(double angleStep, double radiusStep) | ||
{ | ||
Action constructorAction = () => new Spiral(center, angleStep, radiusStep); | ||
constructorAction.Should().Throw<ArgumentException>(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
|
||
<IsPackable>false</IsPackable> | ||
<IsTestProject>true</IsTestProject> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="FluentAssertions" Version="5.10.3" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1"/> | ||
<PackageReference Include="NUnit" Version="3.13.3"/> | ||
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2"/> | ||
<PackageReference Include="NUnit.Analyzers" Version="3.6.1"/> | ||
<PackageReference Include="coverlet.collector" Version="3.2.0"/> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\TagsCloudVizualization\TagsCloudVizualization.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This comment was marked as resolved.
Sorry, something went wrong.