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

Черных Илья #243

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9590cf2
Create projects TagsCloudVisualization, Tests
Ilya4rh Nov 12, 2024
ef5b9d0
Add tdd.sln, tdd.sln.DotSettings
Ilya4rh Nov 12, 2024
c3361d0
Add ICircularCloudLayouter.cs, CircularCloudLayouter.cs
Ilya4rh Nov 12, 2024
9e42a71
Add CircularCloudLayouterTests.cs
Ilya4rh Nov 12, 2024
bd1809c
Реализация добавления первого прямоугольника
Ilya4rh Nov 12, 2024
0747ad9
Изменение в добавлении первого прямоугольника
Ilya4rh Nov 12, 2024
f792052
Добавлена реализация добавления нескольких прямоугольников по спирали…
Ilya4rh Nov 12, 2024
c17e0f9
Убрал отдельную логику добавления первого прямоугольника (излишняя)
Ilya4rh Nov 12, 2024
09efbed
Добавил тест на расположение по спирали
Ilya4rh Nov 12, 2024
2b2988d
Добавил отрисовку получившегося результата
Ilya4rh Nov 12, 2024
5e32c12
Добавил изображения и README.md файл с ним
Ilya4rh Nov 12, 2024
7aa664f
Поправил путь к изображениям в файле README.md
Ilya4rh Nov 12, 2024
42db42a
Поправил README.md
Ilya4rh Nov 12, 2024
07fd1a3
Поправил CircularCloudLayouterTests.cs, добавил Setup
Ilya4rh Nov 12, 2024
0bd2619
Добавил TearDown с визуализацией изображения сломанных тестов
Ilya4rh Nov 12, 2024
2e4a6c9
Поправил класс с визуализацией
Ilya4rh Nov 13, 2024
f92ef27
Добавил интерфейс алгоритма раскладки и класс раскладки по кругу
Ilya4rh Nov 16, 2024
5c9d9b7
Перенес реализацию раскладки по кругу в новый класс
Ilya4rh Nov 16, 2024
353a7f5
Изменил класс CircularCloudLayouter и тесты с появлением нового интер…
Ilya4rh Nov 16, 2024
21dfe52
Изменил название проекта с тестами, класса с тестами и методов внутри
Ilya4rh Nov 16, 2024
66bfc9e
Разделил отрисовку и сохранение изображений
Ilya4rh Nov 16, 2024
492bdc1
Изменил название интерфейса алгоритмов раскладки и сопутствующих классов
Ilya4rh Nov 16, 2024
19ccfbf
Вынес логику вычисления следующей точки расположения
Ilya4rh Nov 17, 2024
ad8646b
Изменил ограничение в конструкторе алгоритма
Ilya4rh Nov 17, 2024
e736a26
Добавил радиус в алгоритм
Ilya4rh Nov 17, 2024
66c278f
Изменил название проекта с тестами
Ilya4rh Nov 17, 2024
61330d3
Изменил тесты расстановки прямоугольников, добавил класс с утилитами
Ilya4rh Nov 17, 2024
c35b934
Изменил метод добавления прямоугольника
Ilya4rh Nov 17, 2024
d9abf43
Добавил новые классы для работы с изображением
Ilya4rh Nov 17, 2024
3274439
Перенес логику отрисовки и сохранения в новые классы
Ilya4rh Nov 17, 2024
d3f04e6
Убрал точку центра из алгоритма расстановки, перенес генерацию прямоу…
Ilya4rh Nov 18, 2024
44309c3
Добавил тесты на алгоритм по расчету точки
Ilya4rh Nov 18, 2024
938e8d8
Правки в классах работы с изображениями
Ilya4rh Nov 18, 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
19 changes: 19 additions & 0 deletions cs/CircularCloudLayouterTests/CircularCloudLayouterTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.5" />
<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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System.Drawing;
using System.Drawing.Imaging;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using TagsCloudVisualization;
using TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

namespace CircularCloudLayouterTests;

[TestFixture]
public class CircularLayoutRectanglesInCloudAlgorithmTests
{
private ICircularCloudLayouter cloudLayouter;
private List<Rectangle> addedRectangles;

[SetUp]
public void Setup()
{
cloudLayouter = new CircularCloudLayouter(new Point(0, 0), new CircularLayoutRectanglesInCloudAlgorithm());
addedRectangles = [];
}

[TearDown]
public void TearDown()
{
if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed)
return;

var pathImageStored = TestContext.CurrentContext.TestDirectory + @"\imageFailedTests";

if (!Directory.Exists(pathImageStored))
{
Directory.CreateDirectory(pathImageStored);
}

var testName = TestContext.CurrentContext.Test.Name;

var bitmap = VisualizationCircularCloudLayout.DrawLayout(addedRectangles, 10);

VisualizationCircularCloudLayout.SaveLayout(bitmap, pathImageStored, $"{testName}.png", ImageFormat.Png);

Console.WriteLine($@"Tag cloud visualization saved to file {pathImageStored}\{testName}.png");
}

[Test]
public void PutNextRectangle_ShouldRectangleInCenter_WhenAddFirstRectangle()
{
var rectangleSize = new Size(10, 15);

var addedRectangle = cloudLayouter.PutNextRectangle(rectangleSize);
var expectedRectangleStartPoint = new Point(-addedRectangle.Width / 2, - addedRectangle.Height / 2);

addedRectangle.Location.Should().BeEquivalentTo(expectedRectangleStartPoint);
}

[TestCase(10, 5, 15)]
[TestCase(50, 30, 100)]
[TestCase(100, 5, 50)]
public void PutNextRectangle_ShouldAddedRectanglesDoNotIntersect(int countRectangles, int minSideLength,
int maxSideLength)
{
var rectangleSizes = GenerateRectangleSizes(countRectangles, minSideLength, maxSideLength);

addedRectangles.AddRange(rectangleSizes.Select(t => cloudLayouter.PutNextRectangle(t)));

for (var i = 0; i < addedRectangles.Count-1; i++)
{
addedRectangles
.Skip(i + 1)
.Any(addedRectangle => addedRectangle.IntersectsWith(addedRectangles[i]))
.Should()
.BeFalse();
}
}

[TestCase(10, 5, 15)]
[TestCase(50, 30, 100)]
[TestCase(100, 5, 50)]
public void CircleShape_ShouldBeCloseToCircle_WhenAddMultipleRectangles(int countRectangles, int minSideLength,
int maxSideLength)
{
var rectangleSizes = GenerateRectangleSizes(countRectangles, minSideLength, maxSideLength);

addedRectangles.AddRange(rectangleSizes.Select(t => cloudLayouter.PutNextRectangle(t)));

var distances = addedRectangles
.Select(rectangle => CalculateDistanceBetweenCenterRectangleAndCenterCloud(rectangle, new Point(0, 0)))
.ToArray();

for (var i = 1; i < distances.Length; i++)
{
var distanceBetweenRectangles = CalculateDistanceBetweenRectangles(addedRectangles[i], addedRectangles[i - 1]);
distances[i].Should().BeApproximately(distances[i - 1], distanceBetweenRectangles);
}
}


private static List<Size> GenerateRectangleSizes(int countRectangles, int minSideLength, int maxSideLength)
{
var random = new Random();

var generatedSizes = Enumerable.Range(0, countRectangles)
.Select(_ => new Size(
random.Next(minSideLength, maxSideLength),
random.Next(minSideLength, maxSideLength))
)
.ToList();

return generatedSizes;
}

private static double CalculateDistanceBetweenCenterRectangleAndCenterCloud(Rectangle rectangle, Point center)
{
var centerRectangle = new Point(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2);

return CalculateDistanceBetweenPoints(centerRectangle, center);
}

private static double CalculateDistanceBetweenRectangles(Rectangle rectangle1, Rectangle rectangle2)
{
var centerRectangle1 = new Point(rectangle1.X + rectangle1.Width / 2, rectangle1.Y + rectangle1.Height / 2);
var centerRectangle2 = new Point(rectangle2.X + rectangle2.Width / 2, rectangle2.Y + rectangle2.Height / 2);

return CalculateDistanceBetweenPoints(centerRectangle1, centerRectangle2);
}

private static double CalculateDistanceBetweenPoints(Point point1, Point point2)

Choose a reason for hiding this comment

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

У тебя ту довольно много статических методов, которые можно вынести в отдельный статический класс-хелпер

{
return Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2));
}
}
23 changes: 23 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Drawing;
using TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

namespace TagsCloudVisualization;

public class CircularCloudLayouter : ICircularCloudLayouter
{
private readonly Point center;
private readonly ILayoutRectanglesInCloudAlgorithm layoutRectanglesInCloudAlgorithm;

public CircularCloudLayouter(Point center, ILayoutRectanglesInCloudAlgorithm layoutRectanglesInCloudAlgorithm)
{
this.center = center;
this.layoutRectanglesInCloudAlgorithm = layoutRectanglesInCloudAlgorithm;
}

public Rectangle PutNextRectangle(Size rectangleSize)

Choose a reason for hiding this comment

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

Сейчас твой метод кладет прямоугольники всегда одним и тем же способом, но в идеале было бы хорошо алгоритм раскладки вынести куда-то в сторону и передавать его в конструктор, чтобы разные экземпляры CircularCloudLayouter могли раскладывать разными способами. Конкретно в этой задаче способ нужен только один, но на будущее лучше сделать класс гибче, и чтобы он почти ничего не знал о том, по какому алгоритму выбираются точки для прямоугольников

{
var rectangle = layoutRectanglesInCloudAlgorithm.PutNextRectangle(rectangleSize, center);

return rectangle;
}
}
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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Drawing;

namespace TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

public class CircularLayoutRectanglesInCloudAlgorithm : ILayoutRectanglesInCloudAlgorithm
{
private readonly List<Rectangle> addedRectangles = [];
private double currentAngleOfCircle;
private double currentRadiusOfCircle;
private const double OneDegree = Math.PI / 180;
private const double FullCircleRotation = 2 * Math.PI;

public Rectangle PutNextRectangle(Size rectangleSize, Point cloudCenter)
{
Rectangle rectangle;

do
{
var x = cloudCenter.X + (int)(currentRadiusOfCircle * Math.Cos(currentAngleOfCircle)) - rectangleSize.Width / 2;
var y = cloudCenter.Y + (int)(currentRadiusOfCircle * Math.Sin(currentAngleOfCircle)) - rectangleSize.Height / 2;
rectangle = new Rectangle(new Point(x, y), rectangleSize);

// увеличиваем угол на 1 градус
currentAngleOfCircle += OneDegree;

// проверяем не прошли ли целый круг

Choose a reason for hiding this comment

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

Было бы круто, если бы шаг радиуса и шаг угла пользователь мог сам выбирать. Например, вводя эти параметры в конструкторе. Но чтобы не нужно было это всегда делать, можно значения по умолчанию поставить

if (currentAngleOfCircle > FullCircleRotation)
{
currentAngleOfCircle = 0;
currentRadiusOfCircle++;
}
} while (IntersectWithAddedRectangles(rectangle));

addedRectangles.Add(rectangle);

return rectangle;
}

private bool IntersectWithAddedRectangles(Rectangle rectangle)
{
return addedRectangles.Any(addedRectangle => addedRectangle.IntersectsWith(rectangle));

Choose a reason for hiding this comment

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

В этом классе нужно оставить только вычисление точек, а логику с прямоугольниками унести в CircularCloudLayouter

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

namespace TagsCloudVisualization.LayoutRectanglesInCloudAlgorithms;

public interface ILayoutRectanglesInCloudAlgorithm
{
Rectangle PutNextRectangle(Size rectangleSize, Point cloudCenter);
}
3 changes: 3 additions & 0 deletions cs/TagsCloudVisualization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
![Визуализация десяти прямоугольников](images/visualization10Rectangles.png)
![Визуализация пятидесяти прямоугольников](images/visualization50Rectangles.png)
![Визуализация ста прямоугольников](images/visualization100Rectangles.png)
17 changes: 17 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

<ItemGroup>
<Folder Include="images\" />
</ItemGroup>

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

namespace TagsCloudVisualization;
#pragma warning disable CA1416

public class VisualizationCircularCloudLayout
{
public static Bitmap DrawLayout(List<Rectangle> rectangles, int paddingFromBorders)
{
var minX = rectangles.Min(rectangle => rectangle.Left);
var minY = rectangles.Min(rectangle => rectangle.Top);
var maxX = rectangles.Max(rectangle => rectangle.Right);
var maxY = rectangles.Max(rectangle => rectangle.Bottom);
var width = maxX - minX + paddingFromBorders;
var height = maxY - minY + paddingFromBorders;

var random = new Random();

var bitmap = new Bitmap(width, height);
using var graphics = Graphics.FromImage(bitmap);

graphics.Clear(Color.White);

foreach (var rectangle in rectangles)
{
var shiftedRectangle = rectangle with
{
X = rectangle.X - minX + paddingFromBorders,
Y = rectangle.Y - minY + paddingFromBorders
};

var randomColor = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256));

var brush = new SolidBrush(randomColor);
graphics.FillRectangle(brush, shiftedRectangle);
graphics.DrawRectangle(Pens.Black, shiftedRectangle);
}

return bitmap;
}

public static void SaveLayout(Bitmap bitmap, string filePath, string fileName, ImageFormat imageFormat)
{
bitmap.Save(Path.Combine(filePath, fileName), imageFormat);
}
}
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.
12 changes: 12 additions & 0 deletions cs/tdd.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ 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", "{E344A7C8-C0E8-4231-BAAD-E2774B855CDF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CircularCloudLayouterTests", "CircularCloudLayouterTests\CircularCloudLayouterTests.csproj", "{86B722F4-6F6A-4773-B2E6-233B10DB7492}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -21,6 +25,14 @@ Global
{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
{E344A7C8-C0E8-4231-BAAD-E2774B855CDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E344A7C8-C0E8-4231-BAAD-E2774B855CDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E344A7C8-C0E8-4231-BAAD-E2774B855CDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E344A7C8-C0E8-4231-BAAD-E2774B855CDF}.Release|Any CPU.Build.0 = Release|Any CPU
{86B722F4-6F6A-4773-B2E6-233B10DB7492}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86B722F4-6F6A-4773-B2E6-233B10DB7492}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86B722F4-6F6A-4773-B2E6-233B10DB7492}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86B722F4-6F6A-4773-B2E6-233B10DB7492}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
3 changes: 3 additions & 0 deletions cs/tdd.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=13FAEF5AD10371439A650EAFEB60E612/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=13FAEF5AD10371439A650EAFEB60E612/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=13FAEF5AD10371439A650EAFEB60E612/Categories/=Imported_002010_002E10_002E2016/@EntryIndexedValue">Imported 10.10.2016</s:String>
Expand Down