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

Алешев Руслан #215

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
99 changes: 99 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using NUnit.Framework;

namespace TagsCloudVisualization
{
public class CircularCloudLayouter
{
private readonly Point center;
private List<Rectangle> cloud;

Choose a reason for hiding this comment

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

Лучше IList, а то и вовсе ICollection. Это позволит не привязываться к реализации коллекции и поменять ее в любой момент


//Spiral coeffs
int i = 0;
float it = (float)Math.PI / 21;
float ri = 50;

Choose a reason for hiding this comment

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

Нужны говорящие имена, модификаторы доступа, мб еще какие-нибудь. Можно даже summary, поясняющее физический смысл полей


public CircularCloudLayouter(Point center)
{
if (center.X <= 0 || center.Y <= 0)
throw new ArgumentException("Central point coordinates should be in positive");
cloud = new List<Rectangle>();

Choose a reason for hiding this comment

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

В CreateCloud всё равно будет создан новый лист. Мб тогда здесь это не нужно

this.center = center;
}

public Rectangle PutNextRectangle(Size rectSize)
{
if (rectSize.Width <= 0 || rectSize.Height <= 0)
throw new ArgumentException("Size width and height should be positive");

if (!cloud.Any())
return new Rectangle(center, rectSize);

Rectangle rect;

while (true)
{
bool findPlace = true;

Choose a reason for hiding this comment

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

Здесь и во многих других местах - var

var point = GetNextPoint();

rect = new Rectangle(new Point(point.X - rectSize.Width / 2, point.Y - rectSize.Height / 2), rectSize);
foreach (var previous in cloud)
{
if (rect.IntersectsWith(previous))
{
findPlace = false;
break;
}
}

Choose a reason for hiding this comment

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

Если знаком с LINQ, можно схлопнуть всю функцию буквально в одну строку


if (findPlace)
break;
}

return rect;
}

public List<Rectangle> CreateCloud(List<Size> rectangleSizes)

Choose a reason for hiding this comment

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

Тут 2 варианта:

  • Либо менять возвращаемый тип на IReadonlyCollection, чтобы никто снаружи при получении списка прямоугольников не мог на него как-то влиять (очистить, например)
  • Либо не возвращать здесь вообще ничего, завести отдельную проперть и использовать ее во всех необходимых местах

Еще, мне как потенциальному пользователю твоего класса не ясна его роль. Например, что если я вызову этот метод 2 раза? Список старых прямоугольников очистится, но функция продолжить работать со старого индекса, получится не оч. Нужно решить как оно должно себя вести - пересоздавать или дополнять и дать соответствующее название

{
cloud = new List<Rectangle>();

foreach (var rectangleSize in rectangleSizes)

Choose a reason for hiding this comment

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

Если знаком с LINQ, можно схлопнуть всю функцию буквально в одну строку

{
cloud.Add(PutNextRectangle(rectangleSize));
}

return cloud;
}

public Image CreateImage()

Choose a reason for hiding this comment

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

Обычно такое называют To[что-нибудь]. Как ToArray ниже, например. Мб ToImage?

{
Bitmap image = new Bitmap(center.X * 2, center.Y * 2);
Graphics gr = Graphics.FromImage(image);
Pen pen = new Pen(Color.White);

gr.Clear(Color.Black);
gr.DrawRectangles(pen, cloud.ToArray());

return image;
}

private Point GetNextPoint()

Choose a reason for hiding this comment

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

Если знаком с перечислениями, было бы наглядно переписать на IEnumerable<Point> с yield return. Таким образом получится спрятать неприятный бесконечный цикл в этой небольшой функции

{
float r = (float)Math.Sqrt(ri * i);
float t = it * i;
float x = (float)(r * Math.Cos(t) + center.X);
float y = (float)(r * Math.Sin(t) + center.Y);
i++;

//Console.WriteLine("{0} {1}", x, y);

return new Point((int)x, (int)y);
}
}
}
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.
32 changes: 32 additions & 0 deletions cs/TagsCloudVisualization/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using System.Drawing;
using TagsCloudVisualization;

namespace TagCloudVisualisation
{
class Program
{
public static void Main()
{
var layouter = new CircularCloudLayouter(new Point(600, 300));

var sizes = new List<Size>();
var rnd = new Random();

for (int i = 0; i < 200; i++)
{
sizes.Add(new Size(rnd.Next(1, 50), rnd.Next(1, 50)));
}

var c = layouter.CreateCloud(sizes);

var image = layouter.CreateImage();

image.Save("cloud.png");
Console.WriteLine("Image saved to file cloud.png");
}
}
}

5 changes: 5 additions & 0 deletions cs/TagsCloudVisualization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Примеры

![](Examples/LandscapeCloud200Rectangles.png)
![](Examples/PortraitCloud200Rectangles.png)
![](Examples/SquareCloud100Rectangles.png)
122 changes: 122 additions & 0 deletions cs/TagsCloudVisualization/TagCloudVisualisationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using FluentAssertions;
using System.Drawing;
using static System.Net.Mime.MediaTypeNames;

namespace TagsCloudVisualization
{
[TestFixture]
public class TagCloudVisualisationTest
{
//[SetUp]
//public void GenerateRandomSizes()
//{

//}

[TestCase(-1, -1)]
[TestCase(1, -1)]
[TestCase(-1, 1)]
[TestCase(0, 0)]
public void CircularCloudLayouterConstructor_ThrowExceptionOnIncorrectCentralPoins(int x, int y)
{
Action a = () => new CircularCloudLayouter(new Point(x, y));
a.Should().Throw<ArgumentException>();
}

[TestCase(1)]
[TestCase(123)]
public void CreateCloud_ReturnCorrectNumberOfRectangles(int rectCount)
{
var layouter = new CircularCloudLayouter(new Point(50, 50));

Choose a reason for hiding this comment

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

  • Давай явно обозначим комментариями где какая часть AAA
  • Объект тестируемого класса часто называются sut (system under testing). Это помогает быстрее ориентироваться в тесте


var sizes = new List<Size>();

for (int i = 0; i < rectCount; i++)
{
sizes.Add(new Size(1, 1));
}

var rects = layouter.CreateCloud(sizes);

rects.Count().Should().Be(rectCount);
}

[Test]
public void PutNextRectangle_ShouldReturnRectangle()
{
var layouter = new CircularCloudLayouter(new Point(50, 50));
layouter.PutNextRectangle(new Size(1, 2)).Should().BeOfType(typeof(Rectangle));
}

[TestCase(1, 1)]
[TestCase(20, 1)]
[TestCase(256, 255)]
[TestCase(1, 20)]
public void PutNextRectangle_ShouldReturnRectangleOfCorrectSize(int width, int height)
{
var layouter = new CircularCloudLayouter(new Point(500, 500));
var rect = layouter.PutNextRectangle(new Size(width, height));
rect.Width.Should().Be(width);
rect.Height.Should().Be(height);
}

[TestCase(-1, 1)]
[TestCase(1, -1)]
[TestCase(0, 1)]
[TestCase(1, 0)]
public void PutNextRectangle_ShouldThrowExceptionOnIncorrectSize(int width, int height)
{
var layouter = new CircularCloudLayouter(new Point(50, 50));
Action a = () => layouter.PutNextRectangle(new Size(width, height));
a.Should().Throw<ArgumentException>();
}

[TestCase(10)]
[TestCase(20)]
[TestCase(200)]
public void CreateCloud_RectanglesShouldNotIntersect(int rectCount)

Choose a reason for hiding this comment

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

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

{
var layouter = new CircularCloudLayouter(new Point(500, 500));
var sizes = new List<Size>();
var rnd = new Random();

for (int i = 0; i < rectCount; i++)
{
sizes.Add(new Size(rnd.Next(1, 100), rnd.Next(1, 100)));
}

var cloud = layouter.CreateCloud(sizes);

for (int i = 0; i < cloud.Count; i++)
{
for (int j = i; j < cloud.Count; j++)
{
if (i == j) continue;
var isIntersect = cloud[i].IntersectsWith(cloud[j]);
if (isIntersect)
{
var img = layouter.CreateImage();

//mark intersection
Graphics gr = Graphics.FromImage(img);
Pen pen = new Pen(Color.Red);
gr.DrawRectangle(pen, cloud[i]);
gr.DrawRectangle(pen, cloud[j]);
string filename = "error - " + DateTime.Now.ToString("H - mm - ss") + ".png";
img.Save(filename);
Console.WriteLine("Tag cloud visualization saved to file {0}", filename);
}

isIntersect.Should().BeFalse();
}
}
}

}
}
19 changes: 19 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34202.233
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudVisualization", "TagsCloudVisualization.csproj", "{85DD6D7B-7811-4117-8B54-A4ECC712D5C8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{85DD6D7B-7811-4117-8B54-A4ECC712D5C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85DD6D7B-7811-4117-8B54-A4ECC712D5C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85DD6D7B-7811-4117-8B54-A4ECC712D5C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85DD6D7B-7811-4117-8B54-A4ECC712D5C8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5BEAA608-2B32-428E-815C-F8F19E0DF0B8}
EndGlobalSection
EndGlobal