-
Notifications
You must be signed in to change notification settings - Fork 305
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
Муканов Арман #223
base: master
Are you sure you want to change the base?
Муканов Арман #223
Changes from 9 commits
dd3f0ba
b933e54
7fbbb0b
5535aae
aab9b6b
a5affb6
16c72d0
5608235
346e6e4
fd377f6
b71f99d
1799420
14c3e2d
4a6a969
66a1e2b
dd6f760
d4c7f5a
ceebdaf
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,90 @@ | ||
using System; | ||
using System.Drawing; | ||
|
||
namespace TagsCloudVisualization; | ||
|
||
public class CircularCloudLayouter : ILayouter | ||
{ | ||
private readonly List<Rectangle> rectangles; | ||
private Point center; | ||
private double spiralStep; | ||
private double angle; | ||
|
||
public CircularCloudLayouter(Point center) | ||
{ | ||
this.center = center; | ||
rectangles = new List<Rectangle>(); | ||
spiralStep = 1; | ||
} | ||
|
||
public IReadOnlyList<Rectangle> AddedRectangles => rectangles; | ||
|
||
public Rectangle PutNextRectangle(Size rectangleSize) | ||
{ | ||
if (rectangleSize.Width == 0 || rectangleSize.Height == 0) | ||
throw new ArgumentException($"{nameof(rectangleSize)} should be with positive width and height"); | ||
var location = GetPosition(rectangleSize); | ||
var rectangle = new Rectangle(location, rectangleSize); | ||
rectangles.Add(rectangle); | ||
return rectangle; | ||
} | ||
|
||
private Point GetPosition(Size rectangleSize) | ||
{ | ||
if (rectangles.Count == 0) | ||
{ | ||
center.Offset(new Point(rectangleSize / -2)); | ||
return center; | ||
} | ||
|
||
return FindApproximatePosition(rectangleSize); | ||
} | ||
|
||
private Point FindApproximatePosition(Size rectangleSize) | ||
{ | ||
var currentAngle = angle; | ||
while (true) | ||
{ | ||
var candidateLocation = new Point(center.X + (int)(spiralStep * Math.Cos(currentAngle)), | ||
center.Y + (int)(spiralStep * Math.Sin(currentAngle))); | ||
var candidateRectangle = new Rectangle(candidateLocation, rectangleSize); | ||
|
||
if (!IntersectsWithAny(candidateRectangle)) | ||
{ | ||
rectangles.Add(candidateRectangle); | ||
angle = currentAngle; | ||
return candidateRectangle.Location; | ||
} | ||
|
||
currentAngle += GetAngleStep(); | ||
if (currentAngle > Math.PI * 2) | ||
{ | ||
currentAngle %= Math.PI * 2; | ||
UpdateSpiral(); | ||
} | ||
} | ||
} | ||
|
||
private bool IntersectsWithAny(Rectangle candidateRectangle) | ||
{ | ||
return rectangles | ||
.Any(candidateRectangle.IntersectsWith); | ||
} | ||
|
||
private void UpdateSpiral() | ||
{ | ||
spiralStep += 1; | ||
} | ||
|
||
private double GetAngleStep() | ||
{ | ||
var defaultStep = Math.PI / 10; | ||
var count = (int)spiralStep / 10; | ||
if (count > 0) | ||
{ | ||
defaultStep /= count; | ||
} | ||
|
||
return defaultStep; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Drawing; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace TagsCloudVisualization | ||
{ | ||
public interface ILayouter | ||
{ | ||
public Rectangle PutNextRectangle(Size rectangleSize); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Microsoft.VisualBasic; | ||
using NUnit.Framework; | ||
using System.Drawing; | ||
using System.Drawing.Imaging; | ||
|
||
namespace TagsCloudVisualization; | ||
|
||
public class LayouterVisualizer | ||
{ | ||
private readonly Bitmap bitmap; | ||
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. Что думаешь насчёт применения свойств здесь? 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. Думаю стоит переделать под свойство. Это даст больше гибкости будущем. Можно выполнять дополнительные действия при обращении или изменении объекта. |
||
|
||
public LayouterVisualizer(Size imageSize) | ||
{ | ||
bitmap = new Bitmap(imageSize.Width, imageSize.Height); | ||
} | ||
|
||
public void VisualizeRectangle(Rectangle rectangle) | ||
{ | ||
using var g = Graphics.FromImage(bitmap); | ||
using var brush = new SolidBrush(Color.White); | ||
|
||
var pen = new Pen(brush, 3); ; | ||
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. Есть ли решение по поводу магического числа 3? 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. Инкапсулировать в свойство. |
||
g.DrawRectangle(pen, rectangle); | ||
} | ||
|
||
public void VisualizeRectangles(IReadOnlyCollection<Rectangle> rectangles) | ||
{ | ||
foreach (var rect in rectangles) | ||
{ | ||
VisualizeRectangle(rect); | ||
} | ||
} | ||
|
||
public void SaveImage(string file, ImageFormat format) | ||
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. Как считаешь нужен ли тут 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. Думаю, что нужен, потому что будет неочевидно, куда сохраняется изображение 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. Речь про 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.
|
||
{ | ||
bitmap.Save(file, format); | ||
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. Какие есть идеи в случае появления ошибок в Save? 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. try catch... |
||
Console.WriteLine($"Tag cloud visualization saved to {file}"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Примеры визуализации облака тегов | ||
|
||
![plot](./TagCloud2.jpeg) | ||
|
||
![plot](./TagCloud3.jpeg) | ||
|
||
![plot](./TagCloud4.jpeg) | ||
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. Как считаешь нужен ли уникальный alt у изображений? Стоит ли подумать о caption или он не нужен? 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. Alt отображается в случае, если изображение не может быть показано. Он поможет понять, что было на изображении, поэтому его стоит использовать. |
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. Как считаешь можно ли хранить файлы в директории с кодом? 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. Думаю, это не очень хорошая практика. Возможные решения:
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. Давай назовём папку а-ля assets/images resources/images и также присвоим названия картинкам более осмысленные? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.3" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23531-01" /> | ||
<PackageReference Include="NUnit" Version="4.0.1" /> | ||
<PackageReference Include="NUnit.Console" Version="3.16.3" /> | ||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" /> | ||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
using System.Drawing; | ||
using System.Drawing.Imaging; | ||
using System.Text.Encodings.Web; | ||
using FluentAssertions; | ||
using Microsoft.VisualStudio.TestPlatform.ObjectModel; | ||
using NUnit.Framework; | ||
using NUnit.Framework.Interfaces; | ||
using NUnit.Framework.Internal; | ||
|
||
namespace TagsCloudVisualization; | ||
|
||
[TestFixture] | ||
public class Tests | ||
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. Стоит иметь на каждый класс свой отдельный файл тестов 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 readonly string path = Path.Combine(TestContext.CurrentContext.TestDirectory, "TestsResults"); | ||
private CircularCloudLayouter cloudLayouter; | ||
private Point center; | ||
private Size canvasSize; | ||
|
||
private Randomizer Random => TestContext.CurrentContext.Random; | ||
|
||
[OneTimeSetUp] | ||
public void OneTimeSetUp() | ||
{ | ||
canvasSize = new Size(900, 900); | ||
center = new Point(450, 450); | ||
|
||
DeleteTestResults(); | ||
} | ||
|
||
[SetUp] | ||
public void SetUp() | ||
{ | ||
cloudLayouter = new CircularCloudLayouter(center); | ||
} | ||
|
||
|
||
[TearDown] | ||
public void TearDown() | ||
{ | ||
var status = TestContext.CurrentContext.Result.Outcome.Status; | ||
var test = TestContext.CurrentContext.Test; | ||
if (status != TestStatus.Failed) return; | ||
|
||
var visualizer = new LayouterVisualizer(canvasSize); | ||
visualizer.VisualizeRectangles(cloudLayouter.AddedRectangles); | ||
|
||
Directory.CreateDirectory(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestsResults")); | ||
var pathToImage = Path.Combine(path, test.Name + ".jpeg"); | ||
visualizer.SaveImage(pathToImage, ImageFormat.Jpeg); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_ShouldThrow_WhenSizeNotPositive() | ||
{ | ||
var action = () => cloudLayouter.PutNextRectangle(new Size(0, 0)); | ||
|
||
action.Should().Throw<ArgumentException>(); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_ShouldReturnRectangleWithGivenSize() | ||
{ | ||
var size = new Size(10, 10); | ||
|
||
var rectangle = cloudLayouter.PutNextRectangle(size); | ||
|
||
rectangle.Size.Should().Be(size); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_ShouldPlaceFirstRectangleInCenter() | ||
{ | ||
var size = new Size(50, 50); | ||
var offsettedCenter = center - size / 2; | ||
|
||
var rectangle = cloudLayouter.PutNextRectangle(size); | ||
|
||
rectangle.Location.Should().Be(offsettedCenter); | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_ShouldReturnNotIntersectingRectangles() | ||
{ | ||
var sizes = GetRandomSizes(70); | ||
|
||
var actualRectangles = sizes.Select(x => cloudLayouter.PutNextRectangle(x)).ToList(); | ||
|
||
for (var i = 0; i < actualRectangles.Count - 1; i++) | ||
{ | ||
for (var j = i + 1; j < actualRectangles.Count; j++) | ||
{ | ||
actualRectangles[i].IntersectsWith(actualRectangles[j]).Should().BeFalse(); | ||
} | ||
} | ||
} | ||
|
||
[Test] | ||
public void PutNextRectangle_ShouldCreateLayoutCloseToCircle() | ||
{ | ||
var sizes = GetRandomSizes(50); | ||
|
||
var rectangles = sizes.Select(size => cloudLayouter.PutNextRectangle(size)).ToList(); | ||
var rectanglesSquare = rectangles | ||
.Select(x => x.Width * x.Height) | ||
.Sum(); | ||
var circleSquare = CalculateBoundingCircleSquare(rectangles); | ||
|
||
rectanglesSquare.Should().BeInRange((int)(circleSquare * 0.8), (int)(circleSquare * 1.2)); | ||
} | ||
|
||
private void DeleteTestResults() | ||
{ | ||
var di = new DirectoryInfo(path); | ||
foreach (var file in di.GetFiles()) | ||
{ | ||
file.Delete(); | ||
} | ||
} | ||
|
||
private Size GetRandomSize() | ||
{ | ||
//return new Size(Random.Next(30, 40), Random.Next(30, 40)); | ||
return new Size(Random.Next(100, 120), Random.Next(30, 90)); | ||
} | ||
|
||
private List<Size> GetRandomSizes(int count) | ||
{ | ||
var sizes = new List<Size>(count); | ||
for (var i = 0; i < count; i++) | ||
sizes.Add(GetRandomSize()); | ||
return sizes; | ||
} | ||
|
||
private double CalculateBoundingCircleSquare(List<Rectangle> rectangles) | ||
{ | ||
var rect = rectangles | ||
.Where(x => x.Contains(x.X, center.Y)) | ||
.MaxBy(x => Math.Abs(x.X - center.X)); | ||
var width = Math.Abs(rect.X - center.X); | ||
|
||
rect = rectangles | ||
.Where(x => x.Contains(center.X, x.Y)) | ||
.MaxBy(x => Math.Abs(x.Y - center.Y)); | ||
var height = Math.Abs(rect.Y - center.Y); | ||
|
||
return Math.Max(width, height) * Math.Max(width, height) * Math.PI; | ||
} | ||
} |
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.
Лучше в отдельные функции унести содержимое условий
А для чисел завести константы
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.
ок