-
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
Вильданов Савелий #241
base: master
Are you sure you want to change the base?
Вильданов Савелий #241
Changes from 22 commits
dee9824
a1d1e33
1fb56fb
16fb3d6
42f7bf7
f86d62f
ef1be52
3415138
38ceb83
4de5007
d20083c
198e49c
069d48f
492c16e
ec49a56
597425e
4dc529e
a1aab21
42d0b33
bbb7ffa
27c162d
5e7cbd6
fdc168c
801bba9
2ecfb96
0119e8d
1a2d746
083d09f
0a052f3
b4d4356
71c304c
a045a06
12d47d1
f16502f
1097f79
0ac7a7c
fb56b45
d625800
3ce2afc
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,3 @@ | ||
![alt text](TagsCloudVisualization/Images/Rectangles%2050.png) | ||
![alt text](TagsCloudVisualization/Images/Rectangles%20100.png) | ||
![alt text](TagsCloudVisualization/Images/Rectangles%20400.png) |
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVisualization; | ||
|
||
public static class CloudGenerator | ||
{ | ||
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); | ||
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. Все ли ресурсы тут освобождены после того, как они стали не нужными? |
||
newGraphics.FillRectangle(backgroundBrush, 0, 0, 1000, 1000); | ||
foreach (var rectangle in rectangles) | ||
{ | ||
newGraphics.DrawRectangle(rectanglePen, rectangle); | ||
} | ||
|
||
bmp.Save(fileName); | ||
} | ||
} |
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; | ||
} |
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); | ||
} |
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); |
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); | ||
} | ||
} |
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> |
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)); | ||
} | ||
} | ||
} |
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 | ||
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. Этот класс тестировать не обязательно (именно ту часть, которая просто вызывает лейаутер и его построение), так как это не основной код твоей программы. Он просто вызывает основные модули и методы в определенном порядке. А после вынесения в А вот тест на сохранение в файл тест можно будет оставить |
||
{ | ||
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)); | ||
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. Это лишняя работа в тесте. Когда тестируем сохраняльщик в файл, то нам не важно, откуда взялись прямоугольники, нам важно просто их нарисовать. Поэтому можешь тут просто руками создать список, лейаутер вызывать не нужно |
||
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); | ||
} | ||
} |
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); | ||
} | ||
} |
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" /> | ||
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. альфа пакеты на свой страх и риск)) |
||
<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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Этот класс взвалил на себя две ответственности - он и прямоугольники генерирует, и в файл сохраняет, а это все же разные ответственности. Давай сохранение в файл вынесем в отдельный модуль. А метод
GenerateRectangles
просто запишем вProgram.cs
как обычный вызывающий код