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 all 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
88 changes: 88 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace TagsCloudVisualization
{
public class CircularCloudLayouter
{
private readonly Point center;
public ICollection<Rectangle> Cloud { get; private set; }
private IPointsProvider pointsProvider;

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

this.center = center;

}

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

if (Cloud == null || !Cloud.Any())
return new Rectangle(center, rectangleSize);

Rectangle rectangle;
bool placingIsCorrect;

var enumerator = pointsProvider.Points().GetEnumerator();
enumerator.MoveNext();

do
{
var point = enumerator.Current;
enumerator.MoveNext();

rectangle = new Rectangle(new Point(point.X - rectangleSize.Width / 2,
point.Y - rectangleSize.Height / 2),
rectangleSize);
placingIsCorrect = PlacedCorrectly(rectangle, Cloud, new Size(center.X * 2, center.Y * 2));

} while (!placingIsCorrect);

return rectangle;
}

public void LayoutRectancles(List<Size> rectangleSizes)
{
Cloud = new List<Rectangle>();
pointsProvider = new SpiralPointsProvider(center);

foreach (var size in rectangleSizes)
Cloud.Add(PutNextRectangle(size));
}

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

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

return image;
}

public bool PlacedCorrectly(Rectangle rectangle, ICollection<Rectangle> rectanglesCloud, Size canvasSize)
{
if (rectangle.Top < 0 || rectangle.Left < 0 || rectangle.Bottom > canvasSize.Height ||
rectangle.Right > canvasSize.Width)
return false;

foreach (var previous in Cloud)
{
if (rectangle.IntersectsWith(previous))
return false;
}

return true;
}
}
}
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.
11 changes: 11 additions & 0 deletions cs/TagsCloudVisualization/IPointsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Drawing;

namespace TagsCloudVisualization
{
internal interface IPointsProvider
{
public IEnumerable<Point> Points();
public void Reset();
}
}
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(300, 300));

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

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

layouter.LayoutRectancles(sizes);

var image = layouter.ToImage();

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)
39 changes: 39 additions & 0 deletions cs/TagsCloudVisualization/SpiralPointsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace TagsCloudVisualization
{
public class SpiralPointsProvider : IPointsProvider
{
private int pointNumber = 0;
private readonly float angleStep = (float)Math.PI / 21;
private readonly float SpiralRadius = 50;
private readonly Point Center;

public SpiralPointsProvider(Point center)
{
Center = center;
}

public IEnumerable<Point> Points()
{
while (pointNumber < 10000000) // Limit number of returned points for safety reason
{
var r = Math.Sqrt(SpiralRadius * pointNumber);
var angle = angleStep * pointNumber;
var x = r * Math.Cos(angle) + Center.X;
var y = r * Math.Sin(angle) + Center.Y;
pointNumber++;
yield return new Point((int)x, (int)y);
}
throw new ArgumentException("Reach end of placing points");
}

public void Reset()
{
pointNumber = 0;
}
}
}
112 changes: 112 additions & 0 deletions cs/TagsCloudVisualization/TagCloudVisualisationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using NUnit.Framework;
using FluentAssertions;
using System.Drawing;
using NUnit.Framework.Interfaces;
using System.Security.Claims;

namespace TagsCloudVisualization
{
[TestFixture]
public class TagCloudVisualisationTest
{
private CircularCloudLayouter layouter;

[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 LayoutRectangles_ReturnCorrectNumberOfRectangles(int rectCount)
{
layouter = new CircularCloudLayouter(new Point(500, 500));

var sizes = new List<Size>();

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

layouter.LayoutRectancles(sizes);

layouter.Cloud.Count().Should().Be(rectCount);
}

[Test]
public void PutNextRectangle_ShouldReturnRectangle()
{
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)
{
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)
{
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 LayoutRectangles_RectanglesShouldNotIntersect(int rectCount)
{
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, 50), rnd.Next(1, 50)));
}

layouter.LayoutRectancles(sizes);

foreach (var rectangleA in layouter.Cloud)
{
foreach (var rectangleB in layouter.Cloud)
{
if (rectangleA == rectangleB) continue;
var isIntersect = rectangleA.IntersectsWith(rectangleB);

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

[TearDown]
public void SaveImageOnTestFails()
{
if (TestContext.CurrentContext.Result.Outcome == ResultState.Failure)
{
var img = layouter.ToImage();
string filename = TestContext.CurrentContext.Test.Name + "_Failed_" + DateTime.Now.ToString("H - mm - ss") + ".png";
img.Save(filename);
}
}
}
}
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