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

Рушкова Ольга2 #261

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8cae611
AddNearestFinder
Nov 13, 2024
09cf278
Add CircularLayer
Nov 13, 2024
9bbe39a
Add CircularCloudLayoter
Nov 13, 2024
dfbc811
Add Direction
Nov 13, 2024
9f88c15
Fix calculation radius
Nov 14, 2024
ce6db8c
Add move to next sector when find position without intersection
Nov 14, 2024
517ccb0
Add intersections and small fix
Nov 14, 2024
3761068
Refactoring: replace validation in layoter, fix tests
Nov 14, 2024
4ec2ddc
Remove usless saving
Nov 15, 2024
fe8c9fe
Refactoring: add RectangleStorage
Nov 15, 2024
d661efd
Refactoring: add using storage
Nov 15, 2024
e4ca17d
Fix: changing rectangle should change rectangle in storage
Nov 15, 2024
8dff187
Add move closer to center
Nov 15, 2024
6f70c9d
Fix radius recalculate and intersection multiplier, add visualisation
Nov 15, 2024
a4217c3
Small fix
Nov 15, 2024
051cb3a
Refactoring: remove dublicate tests
Nov 15, 2024
9879db0
Refactoring And Fix infinite cicle when find position for new rectangle
Nov 17, 2024
badf496
Add results for random generated data
Nov 17, 2024
23112ec
fix filepath
Nov 17, 2024
80508bb
Add third task
Nov 17, 2024
3eb403a
Fix visualizer and add .csproj
Nov 20, 2024
cb4cb27
Fix naming
Nov 20, 2024
9d33c50
Replace DrawGrid to extensions
Nov 20, 2024
d4550a0
Remove storage
Nov 21, 2024
067800a
Fix intelisense and add compressor
Nov 21, 2024
4dfb06b
new realisation for circleLayer
Nov 21, 2024
09726a2
Fix static finder and remove useless tests
Dec 16, 2024
9fd115b
Create project for tests
Dec 16, 2024
adc2df3
Fix circle layer
Dec 16, 2024
066293b
Refactoring
Dec 16, 2024
0f532ed
Change compress algorithm
Dec 16, 2024
c8f2b68
Fix naming
Dec 17, 2024
175d3ca
Delete comment
Dec 17, 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
34 changes: 34 additions & 0 deletions cs/TagsCloudVisualization/BruteForceNearestFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace TagsCloudVisualization
{
public static class BruteForceNearestFinder
{
public static Rectangle? FindNearestByDirection(Rectangle r, Direction direction, List<Rectangle> rectangles)
{
if (rectangles.FirstOrDefault() == default)
return null;
var calculator = GetMinDistanceCalculatorBy(direction);
var nearestByDirection = rectangles
.Select(possibleNearest => (Distance: calculator(possibleNearest, r), Nearest: possibleNearest ))
.Where(el => el.Distance >= 0)
.ToList();

return nearestByDirection.Count > 0 ? nearestByDirection.MinBy(el => el.Distance).Nearest : null;
}

public static Func<Rectangle, Rectangle, int> GetMinDistanceCalculatorBy(Direction direction)

Choose a reason for hiding this comment

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

Чет пересуложненное возвращаемое значение. Почему мы не можем сразу давать результат по дирекшну?

{
return direction switch
{
Direction.Left => (possibleNearest, rectangleForFind) => rectangleForFind.Left - possibleNearest.Right,
Direction.Right => (possibleNearest, rectangleForFind) => possibleNearest.Left - rectangleForFind.Right,
Direction.Top => (possibleNearest, rectangleForFind) => rectangleForFind.Top - possibleNearest.Bottom,
_ => (possibleNearest, rectangleForFind) => possibleNearest.Top - rectangleForFind.Bottom,

Choose a reason for hiding this comment

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

лучше все-таки явно енам тут обрабатывать, чтобы спецэффектов не получить, если я енам расширю

};
}
}
}
42 changes: 42 additions & 0 deletions cs/TagsCloudVisualization/CircleLayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Drawing;

namespace TagsCloudVisualization;

public class CircleLayer

Choose a reason for hiding this comment

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

Неинформативное название. Что такое Layer? звучит как название модели. А судя по телу, это все-таки сервис

{
public Point Center { get; }
public int Radius { get; private set; }

private const int DeltaRadius = 5;
private int currentPositionDegrees;

public CircleLayer(Point center)
{
Center = center;
Radius = 5;
}

private void Expand()
{
Radius += DeltaRadius;
}

Choose a reason for hiding this comment

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

Лучше не выносить. Чтобы лишний раз по коду не бегать


public Point GetNextRectanglePosition()

Choose a reason for hiding this comment

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

Протечка абстракций - сервис ничего не знает про прямоугольники, но мы его заставляем знать про это неявно

{
currentPositionDegrees = GetNextPosition();

Choose a reason for hiding this comment

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

А зачем? Мы же и так возвращаем currentPositionDegrees)

var positionAngleInRadians = currentPositionDegrees * Math.PI / 180.0;
return new Point(Center.X + (int)Math.Ceiling(Radius * Math.Cos(positionAngleInRadians)),
Center.Y + (int)Math.Ceiling(Radius * Math.Sin(positionAngleInRadians)));
}

private int GetNextPosition()
{
if (currentPositionDegrees++ > 360)

Choose a reason for hiding this comment

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

Я даже не обратил внимания, что тут инкремент. Давай явно вынесем увеличение аргумента

{
currentPositionDegrees = 0;
Expand();
}
return currentPositionDegrees;
}
}
65 changes: 65 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace TagsCloudVisualization;

public class CircularCloudLayouter
{
private readonly List<Rectangle> storage;
private readonly CloudCompressor compressor;

public CircleLayer CurrentLayer { get; }

Choose a reason for hiding this comment

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

Почему свойство?


public CircularCloudLayouter(Point center) : this(center, [])
{ }

internal CircularCloudLayouter(Point center, List<Rectangle> storage)

Choose a reason for hiding this comment

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

Протечка абстракций

{
this.storage = storage;
CurrentLayer = new(center);
compressor = new(center, storage);
}

public Rectangle PutNextRectangle(Size nextRectangle)
{
ValidateRectangleSize(nextRectangle);

var inserted = PutRectangleWithoutIntersection(nextRectangle);
var rectangleWithOptimalPosition = compressor.CompressCloudAfterInsertion(inserted);

storage.Add(rectangleWithOptimalPosition);

return rectangleWithOptimalPosition;
}

public Rectangle PutRectangleWithoutIntersection(Size forInsertionSize)
{
var firstRectanglePosition = CurrentLayer.GetNextRectanglePosition();
var forInsertion = new Rectangle(firstRectanglePosition, forInsertionSize);
var intersected = GetRectangleIntersection(forInsertion);

while (intersected != null && intersected.Value != default)
{
var possiblePosition = CurrentLayer.GetNextRectanglePosition();
forInsertion.Location = possiblePosition;

Choose a reason for hiding this comment

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

Лучше не менять поля у существующих объектов без острой необходимости. Будет сложнее понять что поломалось.

Модель легковесная, еще и структура, почему не создать новую?

intersected = GetRectangleIntersection(forInsertion);
}

Choose a reason for hiding this comment

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

Do whlie

return forInsertion;
}

private static void ValidateRectangleSize(Size forInsertion)
{
if (forInsertion.Width <= 0 || forInsertion.Height <= 0)
throw new ArgumentException($"Rectangle has incorrect size: width = {forInsertion.Width}, height = {forInsertion.Height}");
}

private Rectangle? GetRectangleIntersection(Rectangle forInsertion)

Choose a reason for hiding this comment

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

А если несколько пересечений?

Choose a reason for hiding this comment

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

GetOrDefault тогда уж

Choose a reason for hiding this comment

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

Так ли нам важен прямоугольник, с которым пересекается? Или сам факт все таки?

{
if (storage.Count == 0) return null;
return storage.FirstOrDefault(r => forInsertion != r
&& forInsertion.IntersectsWith(r));
}
}
52 changes: 52 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudVisualizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace TagsCloudVisualization
{
public class CircularCloudVisualizer
{
private readonly Color backgroundColor = Color.White;
private readonly Color rectangleColor = Color.DarkBlue;
private readonly Size imageSize;
private readonly List<Rectangle> rectangleStorage;

public CircularCloudVisualizer(List<Rectangle> rectangles, Size imageSize)
{
rectangleStorage = rectangles;
this.imageSize = imageSize;
}

public void CreateImage(string? filePath = null, bool withSaveSteps = false)
{
var rectangles = rectangleStorage.ToArray();

using var image = new Bitmap(imageSize.Width, imageSize.Height);
using var graphics = Graphics.FromImage(image);
graphics.Clear(backgroundColor);
graphics.DrawGrid();
var pen = new Pen(rectangleColor);

for (var i = 0; i < rectangles.Length; i++)
{
var nextRectangle = rectangles[i];
graphics.DrawRectangle(pen, nextRectangle);
Comment on lines +36 to +39

Choose a reason for hiding this comment

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

Прямоугольники рисуются 1к1 к размеру изображения. Из-за этого получим, что при достаточно большом количестве прямоугольников визуализация поломается

if (withSaveSteps)
{
SaveImage(image, filePath);
}
}
SaveImage(image, filePath);
}

Choose a reason for hiding this comment

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

Логику по рисованию и сохранению лучше разделить по SRP.
Но если не просто принципами кидаться, а раскрыть, то:

  • Работа с файлами имеет много специфики. При выборе расширения, например, тебе придется расширять в том числе работу класс по работе с изображением.
  • Любое расширение и кастомизация рисунка влечет за собой изменение класса по сохранению.


private void SaveImage(Bitmap image, string? filePath = null)
{
var rnd = new Random();
filePath ??= Path.Combine(Path.GetTempPath(), $"testImage{rnd.Next()}.png");
image.Save(filePath, ImageFormat.Png);
}
}
}
67 changes: 67 additions & 0 deletions cs/TagsCloudVisualization/CloudCompressor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Drawing;

namespace TagsCloudVisualization
{
internal class CloudCompressor
{
private readonly BruteForceNearestFinder nearestFinder = new ();

Choose a reason for hiding this comment

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

BruteForceNearestFinder - статический класс, не может быть полем.

Choose a reason for hiding this comment

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

Соответственно проект не соберется даже

private readonly Point compressionPoint;
private readonly List<Rectangle> cloud;

public CloudCompressor(Point compressTo, List<Rectangle> cloud)
{
compressionPoint = compressTo;
this.cloud = cloud;
}

public Rectangle CompressCloudAfterInsertion(Rectangle insertionRectangle)
{
var toCompressionPoint = GetDirectionsForMovingForCompress(insertionRectangle);
foreach (var direction in toCompressionPoint)
{
insertionRectangle.Location = CalculateRectangleLocationAfterCompress(insertionRectangle, direction);
}
return insertionRectangle;
}

private Point CalculateRectangleLocationAfterCompress(Rectangle forMoving, Direction toCenter)
{
var nearest = nearestFinder.FindNearestByDirection(forMoving, toCenter, cloud);
if (nearest == null) return forMoving.Location;
var distanceCalculator = nearestFinder.GetMinDistanceCalculatorBy(toCenter);
var distanceForMove = distanceCalculator(nearest.Value, forMoving);
return MoveByDirection(forMoving.Location, distanceForMove, toCenter);
}

private static Point MoveByDirection(Point forMoving, int distance, Direction whereMoving)
{
var factorForDistanceByX = whereMoving switch
{
Direction.Left => -1,
Direction.Right => 1,
_ => 0
};
var factorForDistanceByY = whereMoving switch
{
Direction.Top => -1,
Direction.Bottom => 1,
_ => 0
};
forMoving.X += distance * factorForDistanceByX;
forMoving.Y += distance * factorForDistanceByY;

return forMoving;
}

private List<Direction> GetDirectionsForMovingForCompress(Rectangle forMoving)
{
var directions = new List<Direction>();
if (forMoving.Bottom < compressionPoint.Y) directions.Add(Direction.Bottom);
if (forMoving.Left > compressionPoint.X) directions.Add(Direction.Left);
if (forMoving.Right < compressionPoint.X) directions.Add(Direction.Right);
if (forMoving.Top > compressionPoint.Y) directions.Add(Direction.Top);
return directions;
}

Choose a reason for hiding this comment

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

Кажется, облако не получается достаточно плотным и круглым как раз из-за не очень оптимально выбранного алгоритма сжатия.
Если посмотреть на твои скрины, там можно увидеть пустые места куда можно двигать, но мы почему-то не двигаем.

Choose a reason for hiding this comment

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

Я бы предложил двигать максимально по вектору, как достаточно простое и похожее решение.

}
}
16 changes: 16 additions & 0 deletions cs/TagsCloudVisualization/Dicrection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TagsCloudVisualization
{
public enum Direction
{
Left,
Right,
Top,
Bottom
}
}
27 changes: 27 additions & 0 deletions cs/TagsCloudVisualization/GraphicsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TagsCloudVisualization
{
public static class GraphicsExtensions
{
public static void DrawGrid(this Graphics graphics, int cellsCount = 100, int cellSize = 10)
{
Pen p = new (Color.DarkGray);

for (int y = 0; y < cellsCount; ++y)
{
graphics.DrawLine(p, 0, y * cellSize, cellsCount * cellSize, y * cellSize);
}

for (int x = 0; x < cellsCount; ++x)
{
graphics.DrawLine(p, x * cellSize, 0, x * cellSize, cellsCount * cellSize);
}
}
}
}
Binary file added cs/TagsCloudVisualization/Images/FIRSTa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/TagsCloudVisualization/Images/sECOND.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cs/TagsCloudVisualization/Images/third.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions cs/TagsCloudVisualization/PointExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TagsCloudVisualization
{
public static class PointExtensions
{
public static int CalculateDistanceBetween(this Point current, Point other)
{
return (int)Math.Ceiling(Math.Sqrt((current.X - other.X) * (current.X - other.X) + (current.Y - other.Y) * (current.Y - other.Y)));
}
}
}
3 changes: 3 additions & 0 deletions cs/TagsCloudVisualization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
![Alt text](./Images/FIRSTa.png?raw=true "First")
![Alt text](./Images/sECOND.png?raw=true "Second")
![Alt text](./Images/third.png?raw=true "Third")
23 changes: 23 additions & 0 deletions cs/TagsCloudVisualization/TagsCloudVisualization.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<StartupObject>TagsCloudVisualization.Program</StartupObject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="Images\ImagesTest\" />
</ItemGroup>

</Project>
38 changes: 38 additions & 0 deletions cs/TagsCloudVisualization/Tests/BruteForceNearestFinderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using NUnit.Framework;
using FluentAssertions;
using System.Drawing;

namespace TagsCloudVisualization.Tests
{
public class BruteForceNearestFinderTests
{
[Test]
public void FindNearest_ShouldReturnNull_OnEmptyRectangles()
{
var rectangleForFind = new Rectangle(new Point(5, 7), new Size(4, 2));

BruteForceNearestFinder.FindNearestByDirection(rectangleForFind, Direction.Top, []).Should().BeNull();
}

[TestCase(4, 10, Direction.Top)]
[TestCase(2, 7, Direction.Top, true)]
[TestCase(2, 7, Direction.Right)]
[TestCase(0, 0, Direction.Right, true)]
[TestCase(0, 0, Direction.Bottom, true)]
[TestCase(7, 4, Direction.Bottom)]
[TestCase(10, 11, Direction.Left)]
[TestCase(7, 4, Direction.Left, true)]
public void FindNearest_ShouldReturnNearestRectangleByDirection_ForArgumentRectangle(int x, int y, Direction direction, bool isFirstNearest = false)
{
var addedRectangle1 = new Rectangle(new Point(2, 2), new Size(3, 4));
var addedRectangle2 = new Rectangle(new Point(5, 7), new Size(4, 2));
var rectangleForFind = new Rectangle(new Point(x, y), new Size(2, 1));
var rectangles = new List<Rectangle> { addedRectangle1, addedRectangle2 };

var nearest = BruteForceNearestFinder.FindNearestByDirection(rectangleForFind, direction, rectangles);

nearest.Should().Be(isFirstNearest ? addedRectangle1 : addedRectangle2);
}
}
}
Loading