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

Вильданов Савелий #241

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
dee9824
create interface
Saveliy21 Nov 12, 2024
a1d1e33
Add CircularCloudLayotherTest
Saveliy21 Nov 12, 2024
1fb56fb
add CircularCloudLayouter
Saveliy21 Nov 12, 2024
16fb3d6
Add SpiralTest
Saveliy21 Nov 12, 2024
42f7bf7
Add class Spiral
Saveliy21 Nov 12, 2024
f86d62f
add settings
Saveliy21 Nov 12, 2024
ef1be52
refactor CircularCloudLayouter.cs
Saveliy21 Nov 15, 2024
3415138
refactor Spiral.cs
Saveliy21 Nov 15, 2024
38ceb83
refactor CircularCloudLayotherTest.cs
Saveliy21 Nov 15, 2024
4de5007
refactor SpiralTest.cs
Saveliy21 Nov 15, 2024
d20083c
refactor project Tests
Saveliy21 Nov 15, 2024
198e49c
change Spiral settings
Saveliy21 Nov 16, 2024
069d48f
create CloudGenerator.cs
Saveliy21 Nov 16, 2024
492c16e
create Program.cs
Saveliy21 Nov 16, 2024
ec49a56
Add Images
Saveliy21 Nov 16, 2024
597425e
small refactor Program.cs
Saveliy21 Nov 16, 2024
4dc529e
change Images location
Saveliy21 Nov 16, 2024
a1aab21
create class for constants
Saveliy21 Nov 16, 2024
42d0b33
refactor CloudGenerator.cs
Saveliy21 Nov 16, 2024
bbb7ffa
Add TearDown
Saveliy21 Nov 16, 2024
27c162d
refactor SpiralTest.cs
Saveliy21 Nov 16, 2024
5e7cbd6
Add CloudGeneratorTest.cs
Saveliy21 Nov 16, 2024
fdc168c
Add new class SaveImages
Saveliy21 Nov 21, 2024
801bba9
change FluentAssertions version
Saveliy21 Nov 21, 2024
2ecfb96
refactor CircularCloudLayotherTest.cs
Saveliy21 Nov 21, 2024
0119e8d
replace CloudGenerator.cs to CloudDrawer.cs
Saveliy21 Nov 21, 2024
1a2d746
replace CloudGeneratorTest to CloudDrawerTest
Saveliy21 Nov 21, 2024
083d09f
refactor Program.cs
Saveliy21 Nov 21, 2024
0a052f3
add new method to CircularCloudLayouter.cs
Saveliy21 Nov 22, 2024
b4d4356
refactor CloudDrawer.cs
Saveliy21 Nov 22, 2024
71c304c
rename class
Saveliy21 Nov 22, 2024
a045a06
refactor Program.cs
Saveliy21 Nov 22, 2024
12d47d1
refactor TagsCloudVisualizationTests.csproj
Saveliy21 Nov 22, 2024
f16502f
refactor TagsCloudVisualization.csproj
Saveliy21 Nov 22, 2024
1097f79
rename CloudDrawerTest.cs
Saveliy21 Nov 22, 2024
0ac7a7c
refactor CircularCloudLayotherTest.cs
Saveliy21 Nov 22, 2024
fb56b45
unit SaveImage.cs with CloudDrawer.cs
Saveliy21 Nov 22, 2024
d625800
refactor
Saveliy21 Nov 22, 2024
3ce2afc
small refactor
Saveliy21 Nov 22, 2024
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
3 changes: 3 additions & 0 deletions cs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
![alt text](TagsCloudVisualization/Images/Rectangles%2050.png)
![alt text](TagsCloudVisualization/Images/Rectangles%20100.png)
![alt text](TagsCloudVisualization/Images/Rectangles%20400.png)
41 changes: 41 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Drawing;

namespace TagsCloudVisualization;

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 List<Rectangle>();
spiral = new Spiral(center);
}

public Rectangle PutNextRectangle(Size rectangleSize)
{
Rectangle rectangle;
if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0)
{
throw new ArgumentException($"Rectangle size ({rectangleSize}) should be positive");
}

do
{
Point point = spiral.GetNextPointOnSpiral();
point.Offset(-rectangleSize.Width / 2, -rectangleSize.Height / 2);
rectangle = new Rectangle(point, rectangleSize);
} while (rectangles.Any(r => r.IntersectsWith(rectangle)));

rectangles.Add(rectangle);
return rectangle;
}

public List<Rectangle> GetRectangles()
{
return rectangles;
}
}
36 changes: 36 additions & 0 deletions cs/TagsCloudVisualization/CloudGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Drawing;

namespace TagsCloudVisualization;

public static class CloudGenerator

Choose a reason for hiding this comment

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

Этот класс взвалил на себя две ответственности - он и прямоугольники генерирует, и в файл сохраняет, а это все же разные ответственности. Давай сохранение в файл вынесем в отдельный модуль. А метод GenerateRectangles просто запишем в Program.cs как обычный вызывающий код

{
public static List<Rectangle> GenerateRectangles(int count)
{
CircularCloudLayouter circularCloudLayouter =
new CircularCloudLayouter(new Point(CloudLayouterConst.CloudCentreX, CloudLayouterConst.CloudCentreY));
Random rnd = new Random();
for (int i = 0; i < count; i++)
{
int width = rnd.Next(10, 25);
int height = rnd.Next(10, 25);
circularCloudLayouter.PutNextRectangle(new Size(width, height));
}

return circularCloudLayouter.GetRectangles();
}

public static void DrawRectangles(List<Rectangle> rectangles, string fileName)
{
Bitmap bmp = new Bitmap(1000, 1000);
Graphics newGraphics = Graphics.FromImage(bmp);
var backgroundBrush = new SolidBrush(Color.White);
var rectanglePen = new Pen(Color.Black);

Choose a reason for hiding this comment

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

Все ли ресурсы тут освобождены после того, как они стали не нужными?

newGraphics.FillRectangle(backgroundBrush, 0, 0, 1000, 1000);
foreach (var rectangle in rectangles)
{
newGraphics.DrawRectangle(rectanglePen, rectangle);
}

bmp.Save(fileName);
}
}
7 changes: 7 additions & 0 deletions cs/TagsCloudVisualization/CloudLayouterConst.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TagsCloudVisualization;

public class CloudLayouterConst
{
public const int CloudCentreX = 500;
public const int CloudCentreY = 500;
}
8 changes: 8 additions & 0 deletions cs/TagsCloudVisualization/ICircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Drawing;

namespace TagsCloudVisualization;

public interface ICircularCloudLayouter
{
Rectangle PutNextRectangle(Size rectangleSize);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions cs/TagsCloudVisualization/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using TagsCloudVisualization;

const string fileName1 = "Rectangles 50.png";
const string fileName2 = "Rectangles 100.png";
const string fileName3 = "Rectangles 400.png";

CloudGenerator.DrawRectangles(CloudGenerator.GenerateRectangles(50), fileName1);
CloudGenerator.DrawRectangles(CloudGenerator.GenerateRectangles(100), fileName2);
CloudGenerator.DrawRectangles(CloudGenerator.GenerateRectangles(400), fileName3);
26 changes: 26 additions & 0 deletions cs/TagsCloudVisualization/Spiral.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Drawing;

namespace TagsCloudVisualization;

public class Spiral
{
private readonly Point center;
private readonly double angleStep;
private double angle;


public Spiral(Point center, double angleStep = 0.01)
{
this.center = center;
this.angleStep = angleStep;
angle = 0;
}

public Point GetNextPointOnSpiral()
{
angle += angleStep;
var x = (int) (center.X + angle * Math.Cos(angle));
var y = (int) (center.Y + angle * Math.Sin(angle));
return new Point(x, y);
}
}
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>
<RootNamespace>Cloud</RootNamespace>
</PropertyGroup>

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

</Project>
99 changes: 99 additions & 0 deletions cs/TagsCloudVisualizationTests/CircularCloudLayotherTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System.Drawing;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Interfaces;

namespace TagsCloudVisualization.TagsCloudVisualizationTests;

public class CircularCloudLayotherTest
{
private CircularCloudLayouter _circularCloudLayouter;

[SetUp]
public void SetUp()
{

_circularCloudLayouter =
new CircularCloudLayouter(new Point(CloudLayouterConst.CloudCentreX, CloudLayouterConst.CloudCentreY));
}

[TearDown]
public void TearDown()
{
if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed) return;
var testName = TestContext.CurrentContext.Test.Name;
var filePath = TestContext.CurrentContext.WorkDirectory;
CloudGenerator.DrawRectangles(_circularCloudLayouter.GetRectangles(), $"{testName}.png");
Console.WriteLine($"Tag cloud visualization saved to file {filePath}");
}

[TestCase(-2, 3, TestName = "Negative width")]
[TestCase(2, -3, TestName = "Negative height")]
[TestCase(0, 0, TestName = "zero width and height")]
[TestCase(-2, -3, TestName = "Negative weight and height")]
public void PutNextRectangle_ShouldThrowArgumentException_WithNegativeInput(int width, int height)
{
Action action = () => new CircularCloudLayouter(new Point()).PutNextRectangle(new Size(width, height));
action.Should().Throw<ArgumentException>();
}

[TestCase(2, 3, TestName = "positive width and height")]
public void PutNextRectangle_ShouldNotThrowArgumentException_WithCorrectInput(int width, int height)
{
Action action = () => new CircularCloudLayouter(new Point()).PutNextRectangle(new Size(width, height));
action.Should().NotThrow<ArgumentException>();
}

[Test]
public void PutNextRectangle_ShouldAddNewRectangle()
{
var size = new Size(10, 20);
var coordinateX = CloudLayouterConst.CloudCentreX - size.Width / 2;
var coordinateY = CloudLayouterConst.CloudCentreY - size.Height / 2;
_circularCloudLayouter.PutNextRectangle(size);
_circularCloudLayouter.GetRectangles().Should().NotBeEmpty();
_circularCloudLayouter.GetRectangles().Should()
.Contain(new Rectangle(coordinateX, coordinateY, size.Width, size.Height));
}

[Test]
public void CircularCloudLayouter_RectAngelsListShouldHaveCorrectSize()
{
_circularCloudLayouter.PutNextRectangle(new Size(40, 20));
_circularCloudLayouter.PutNextRectangle(new Size(20, 40));
_circularCloudLayouter.PutNextRectangle(new Size(50, 50));
_circularCloudLayouter.GetRectangles().Count.Should().Be(3);
}

[Test]
public void CircularCloudLayouter_RectAngelsShouldNoIntersectsWithOthers()
{
Random rnd = new Random();
for (int i = 0; i < 100; i++)
{
int width = rnd.Next(15, 40);
int height = rnd.Next(15, 40);
_circularCloudLayouter.PutNextRectangle(new Size(width, height));
}

List<Rectangle> rectangels = _circularCloudLayouter.GetRectangles();
foreach (Rectangle rectangle in rectangels)
{
rectangels.Where((_, j) => j != rectangels.IndexOf(rectangle))
.All(r => !r.IntersectsWith(rectangle))
.Should().BeTrue();
}
}

[Test, MaxTime(5000)]
public void CircularCloudLayouter_ShouldWorkInTime()
{
Random rnd = new Random();
for (int i = 0; i < 10000; i++)
{
int width = rnd.Next(1, 10);
int height = rnd.Next(1, 10);
_circularCloudLayouter.PutNextRectangle(new Size(width, height));
}
}
}
38 changes: 38 additions & 0 deletions cs/TagsCloudVisualizationTests/CloudGeneratorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Drawing;
using FluentAssertions;
using NUnit.Framework;
using static System.IO.File;

namespace TagsCloudVisualization.TagsCloudVisualizationTests;

public class CloudGeneratorTest

Choose a reason for hiding this comment

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

Этот класс тестировать не обязательно (именно ту часть, которая просто вызывает лейаутер и его построение), так как это не основной код твоей программы. Он просто вызывает основные модули и методы в определенном порядке. А после вынесения в Program.cs и тестировать будет нечего))

А вот тест на сохранение в файл тест можно будет оставить

{
private const string FileName = "Picture.png";

[TearDown]
public void TearDown()
{
if (Exists(FileName))
{
Delete(FileName);
}
}

[Test]
public void DrawRectangles_ShouldDrawImage()
{
CircularCloudLayouter circularCloudLayouter =
new CircularCloudLayouter(new Point(CloudLayouterConst.CloudCentreX, CloudLayouterConst.CloudCentreY));
circularCloudLayouter.PutNextRectangle(new Size(2, 2));

Choose a reason for hiding this comment

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

Это лишняя работа в тесте. Когда тестируем сохраняльщик в файл, то нам не важно, откуда взялись прямоугольники, нам важно просто их нарисовать. Поэтому можешь тут просто руками создать список, лейаутер вызывать не нужно

CloudGenerator.DrawRectangles(circularCloudLayouter.GetRectangles(), FileName);
Exists(FileName).Should().BeTrue();
}

[Test]
public void GenerateRectangles_ShouldReturnList_WithCorrectSize()
{
List<Rectangle> list = CloudGenerator.GenerateRectangles(200);
list.Should().NotBeNullOrEmpty();
list.Count.Should().Be(200);
}
}
28 changes: 28 additions & 0 deletions cs/TagsCloudVisualizationTests/SpiralTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Drawing;
using FluentAssertions;
using NUnit.Framework;

namespace TagsCloudVisualization.TagsCloudVisualizationTests;

public class SpiralTest
{
[Test]
public void GetNextPointOnSpiral_ShouldReturnPointsOnSpiral()
{
Spiral spiral = new Spiral(new Point(CloudLayouterConst.CloudCentreX, CloudLayouterConst.CloudCentreY));
List<Point> expected =
[
new Point(500, 500), new Point(500, 500), new Point(499, 501), new Point(497, 500), new Point(497, 496),
new Point(501, 495), new Point(505, 498)
];
List<Point> points = [];
for (int i = 0; i < 700; i++)
{
var point = spiral.GetNextPointOnSpiral();
if (i % 100 == 0)
points.Add(point);
}

points.Should().BeEquivalentTo(expected);
}
}
20 changes: 20 additions & 0 deletions cs/TagsCloudVisualizationTests/TagsCloudVisualizationTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.5" />

Choose a reason for hiding this comment

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

альфа пакеты на свой страх и риск))

<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="4.2.2" />
</ItemGroup>

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

</Project>
8 changes: 6 additions & 2 deletions cs/tdd.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BowlingGame", "BowlingGame\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{B5108E20-2ACF-4ED9-84FE-2A718050FC94}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudVisualization", "TagsCloudVisualization\TagsCloudVisualization.csproj", "{43089B42-005E-43E9-8E19-544E3534C010}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AD0F018A-732E-4074-8527-AB2EEC8D0BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD0F018A-732E-4074-8527-AB2EEC8D0BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD0F018A-732E-4074-8527-AB2EEC8D0BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD0F018A-732E-4074-8527-AB2EEC8D0BF3}.Release|Any CPU.Build.0 = Release|Any CPU
{B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5108E20-2ACF-4ED9-84FE-2A718050FC94}.Release|Any CPU.Build.0 = Release|Any CPU
{43089B42-005E-43E9-8E19-544E3534C010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43089B42-005E-43E9-8E19-544E3534C010}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43089B42-005E-43E9-8E19-544E3534C010}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43089B42-005E-43E9-8E19-544E3534C010}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down