Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
57fdb3e
feat: add TagsCloudVisualization project
tazik23 Nov 26, 2025
e79a95c
feat: add geometry classes
tazik23 Nov 26, 2025
e03857d
feat: add circular cloud layouter
tazik23 Nov 26, 2025
c20de37
feat: add rectnagles intersection check method
tazik23 Nov 26, 2025
58a6ea1
test: add test to check first rectangle position
tazik23 Nov 26, 2025
be5bbc1
test: add test to check intersections of rectangles
tazik23 Nov 26, 2025
fd9bb94
feat: add spiral class
tazik23 Nov 26, 2025
2eedf4a
feat: add realization for PutNextRectangleMethod
tazik23 Nov 26, 2025
1c254e5
feat: add extensions methods for point and rectangle
tazik23 Nov 26, 2025
8f5e7f2
test: add test to check tight distribution
tazik23 Nov 26, 2025
839d8fc
test: add more test cases
tazik23 Nov 27, 2025
533923c
feat: add TryMoveToCenter method
tazik23 Nov 27, 2025
ab0a7cc
feat: add setting for cloud visualizer
tazik23 Nov 27, 2025
e917441
feat: add Bitmap cloud visualizer
tazik23 Nov 27, 2025
1bb00b4
feat: add Bitmap cloud saver
tazik23 Nov 27, 2025
135f823
fix: move GenerateSizes in separate class
tazik23 Nov 27, 2025
10c3ea9
fix: fix BorderWidth in default settings
tazik23 Nov 27, 2025
1091de3
test: now save cloud visualization on test failure
tazik23 Nov 27, 2025
69afde4
feat: add HasIntersectionsMethod
tazik23 Nov 27, 2025
e55e855
fix: refactor TryMoveToCenter method
tazik23 Nov 27, 2025
88f4206
test: add big rectangles test case
tazik23 Nov 27, 2025
36b947e
fix: make project library
tazik23 Nov 27, 2025
8cdabba
feat: add generated clouds
tazik23 Nov 27, 2025
a270c9a
fix: rename folder and move generator into it
tazik23 Nov 27, 2025
5eb9eb0
fix: fix center declaration
tazik23 Nov 27, 2025
a3c0aab
feat: add README.md
tazik23 Nov 27, 2025
5b7b6a7
test: add small rectangles test case
tazik23 Nov 27, 2025
a552fdf
fix: remove redundant classes
tazik23 Dec 1, 2025
148b0e1
fix: fix GetVertices and GetArea method
tazik23 Dec 1, 2025
a2302cc
fix: add GetCenter method and fix MoveInDirection
tazik23 Dec 1, 2025
bf8af39
feat: add ISpiral interface
tazik23 Dec 1, 2025
91d3278
fix: fix moveInDirection method
tazik23 Dec 1, 2025
26db1d2
fix: fix layouter
tazik23 Dec 1, 2025
c880433
feat: fix TryMoveToCenter method
tazik23 Dec 1, 2025
347a631
fix: fix visualizer
tazik23 Dec 1, 2025
87a411d
fix: update maxIterations count
tazik23 Dec 1, 2025
b5defbf
fix: move size generator to Test project and add more generating methods
tazik23 Dec 1, 2025
6a520a5
fix: move tests into different files, added clear title to test cases
tazik23 Dec 1, 2025
7d47914
fix: fix bitmap saver
tazik23 Dec 1, 2025
493ab54
fix: fix naming
tazik23 Dec 1, 2025
11d6fc0
fix: fix naming
tazik23 Dec 2, 2025
7494571
test: add PerfomanceTest
tazik23 Dec 2, 2025
6dbb8f1
fix: fix naming
tazik23 Dec 2, 2025
c6753a2
fix: move magic number to constant
tazik23 Dec 2, 2025
b235b48
fix: fix sizes
tazik23 Dec 2, 2025
504d578
feat: add quad tree for optimization
tazik23 Dec 2, 2025
cc8fb60
fix: apply reformating
tazik23 Dec 2, 2025
57781f0
feat: add clouds
tazik23 Dec 2, 2025
f4fa86e
feat: add clouds
tazik23 Dec 2, 2025
66fe302
Merge remote-tracking branch 'origin/homework/tags-cloud-visualizatio…
tazik23 Dec 2, 2025
0c612d3
fix: fix test naming
tazik23 Dec 2, 2025
5885ce3
fix: refactor TryMoveAlongAxis method
tazik23 Dec 2, 2025
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
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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions cs/TagsCloudVisualization/Geometry/Axis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TagsCloudVisualization.Geometry;

public enum Axis
{
X,
Y
}
19 changes: 19 additions & 0 deletions cs/TagsCloudVisualization/Geometry/Extensions/PointExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Drawing;

namespace TagsCloudVisualization.Geometry.Extensions;

public static class PointExtensions
{
public static double DistanceTo(this Point point, Point other)
{
var dx = point.X - other.X;
var dy = point.Y - other.Y;

return Math.Sqrt(dx * dx + dy * dy);
}

public static bool IsZero(this Point point)
{
return Math.Abs(point.X) < double.Epsilon && Math.Abs(point.Y) < double.Epsilon;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Drawing;

namespace TagsCloudVisualization.Geometry.Extensions;

public static class RectangleExtensions
{
public static IEnumerable<Point> GetVertices(this Rectangle rectangle)
{
yield return new Point(rectangle.Left, rectangle.Top);
yield return new Point(rectangle.Left, rectangle.Bottom);
yield return new Point(rectangle.Right, rectangle.Top);
yield return new Point(rectangle.Right, rectangle.Bottom);
}

public static double GetArea(this Rectangle rectangle)
{
return rectangle.Height * rectangle.Width;
}

public static Point GetCenter(this Rectangle rectangle)
{
return new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2);
}

public static Rectangle MoveInDirection(this Rectangle rectangle, Point direction, int distance)
{
var newLocation = new Point(rectangle.Left + direction.X * distance, rectangle.Top + direction.Y * distance);
return new Rectangle(newLocation, rectangle.Size);
}
}
9 changes: 9 additions & 0 deletions cs/TagsCloudVisualization/Geometry/ISpiral.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Drawing;

namespace TagsCloudVisualization.Geometry;

public interface ISpiral
{
Point Center { get; }
Point GetNextPoint();
}
30 changes: 30 additions & 0 deletions cs/TagsCloudVisualization/Geometry/Spiral.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Drawing;

namespace TagsCloudVisualization.Geometry;

public class ArchimedeanSpiral : ISpiral
{
private double currentAngle;
private readonly double angleStep;
private readonly int spiralStep;

public Point Center { get; }

public ArchimedeanSpiral(Point center, double angleStep = 0.1, int spiralStep = 1)
{
Center = center;
this.angleStep = angleStep;
this.spiralStep = spiralStep;
}

public Point GetNextPoint()
{
var radius = spiralStep / (2 * Math.PI) * currentAngle;
var x = (int)(Center.X + radius * Math.Cos(currentAngle));
var y = (int)(Center.Y + radius * Math.Sin(currentAngle));

currentAngle += angleStep;

return new Point(x, y);
}
}
12 changes: 12 additions & 0 deletions cs/TagsCloudVisualization/Helpers/BitmapCloudSaver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Drawing;
using System.Drawing.Imaging;

namespace TagsCloudVisualization.Helpers;

public class BitmapCloudSaver
{
public void SaveToFile(Image bitmap, string fileName, ImageFormat format)
{
bitmap.Save(fileName, format);
}
}
9 changes: 9 additions & 0 deletions cs/TagsCloudVisualization/Layouters/ICircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Drawing;

namespace TagsCloudVisualization.Layouters;

public interface ICircularCloudLayouter
{
IReadOnlyList<Rectangle> Rectangles { get; }
Rectangle PutNextRectangle(Size rectangleSize);
}
188 changes: 188 additions & 0 deletions cs/TagsCloudVisualization/Layouters/QuadTree.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
using System.Drawing;

namespace TagsCloudVisualization.Layouters;

public class QuadTree
{
private readonly List<Rectangle> elements = new();
private readonly int bucketCapacity;
private readonly int maxDepth;
private readonly int level;

private QuadTree? upperLeft;
private QuadTree? upperRight;
private QuadTree? bottomLeft;
private QuadTree? bottomRight;

private Rectangle bounds;

public bool IsLeaf => upperLeft == null;

public QuadTree(Rectangle bounds, int bucketCapacity = 32, int maxDepth = 5, int level = 0)
{
this.bounds = bounds;
this.bucketCapacity = bucketCapacity;
this.maxDepth = maxDepth;
this.level = level;
}

public void Insert(Rectangle element)
{
if (!bounds.Contains(element))
ExpandBounds(element);

if (elements.Count >= bucketCapacity)
Split();

var containingChild = GetContainingChild(element);

if (containingChild != null)
containingChild.Insert(element);
else
elements.Add(element);
}

public bool HasIntersection(Rectangle element)
{
var nodes = new Queue<QuadTree>();
nodes.Enqueue(this);

while (nodes.Count > 0)
{
var node = nodes.Dequeue();

if (!element.IntersectsWith(node.bounds))
continue;

foreach (var item in node.elements)
if (item != element && element.IntersectsWith(item))
return true;

if (node.IsLeaf) continue;

if (node.upperLeft != null && element.IntersectsWith(node.upperLeft.bounds))
nodes.Enqueue(node.upperLeft);

if (node.upperRight != null && element.IntersectsWith(node.upperRight.bounds))
nodes.Enqueue(node.upperRight);

if (node.bottomLeft != null && element.IntersectsWith(node.bottomLeft.bounds))
nodes.Enqueue(node.bottomLeft);

if (node.bottomRight != null && element.IntersectsWith(node.bottomRight.bounds))
nodes.Enqueue(node.bottomRight);
}

return false;
}

private void Clear()
{
elements.Clear();
upperLeft = upperRight = bottomLeft = bottomRight = null;
}

private void ExpandBounds(Rectangle newElement)
{
var minX = Math.Min(bounds.X, newElement.X);
var minY = Math.Min(bounds.Y, newElement.Y);
var maxX = Math.Max(bounds.Right, newElement.Right);
var maxY = Math.Max(bounds.Bottom, newElement.Bottom);

var width = maxX - minX;
var height = maxY - minY;

var newWidth = width * 2;
var newHeight = height * 2;

var centerX = (minX + maxX) / 2;
var centerY = (minY + maxY) / 2;

var newBounds = new Rectangle(
centerX - newWidth / 2,
centerY - newHeight / 2,
newWidth,
newHeight);

RebuildWithNewBounds(newBounds);
}

private void RebuildWithNewBounds(Rectangle newBounds)
{
var allElements = GetAllElements();

Clear();

bounds = newBounds;

foreach (var element in allElements) Insert(element);
}

private List<Rectangle> GetAllElements()
{
var result = new List<Rectangle>(elements);

if (!IsLeaf)
{
result.AddRange(upperLeft.GetAllElements());
result.AddRange(upperRight.GetAllElements());
result.AddRange(bottomLeft.GetAllElements());
result.AddRange(bottomRight.GetAllElements());
}

return result;
}

private void Split()
{
if (!IsLeaf)
return;

if (level + 1 > maxDepth)
return;

var halfWidth = bounds.Width / 2;
var halfHeight = bounds.Height / 2;
var x = bounds.X;
var y = bounds.Y;

upperLeft = new QuadTree(
new Rectangle(x, y, halfWidth, halfHeight),
bucketCapacity, maxDepth, level + 1);

upperRight = new QuadTree(
new Rectangle(x + halfWidth, y, halfWidth, halfHeight),
bucketCapacity, maxDepth, level + 1);

bottomLeft = new QuadTree(
new Rectangle(x, y + halfHeight, halfWidth, halfHeight),
bucketCapacity, maxDepth, level + 1);

bottomRight = new QuadTree(
new Rectangle(x + halfWidth, y + halfHeight, halfWidth, halfHeight),
bucketCapacity, maxDepth, level + 1);

var elements = this.elements.ToList();

foreach (var element in elements)
{
var containingChild = GetContainingChild(element);
if (containingChild != null)
{
this.elements.Remove(element);
containingChild.Insert(element);
}
}
}

private QuadTree? GetContainingChild(Rectangle element)
{
if (IsLeaf) return null;

foreach (var child in new[] { upperLeft, upperRight, bottomLeft, bottomRight })
if (child != null && child.bounds.Contains(element))
return child;

return null;
}
}
Loading