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

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

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

namespace TagsCloudVisualization
{
public class BruteForceNearestFinder
{
private List<Rectangle> rectangles = new();

public void Insert(Rectangle r)

Choose a reason for hiding this comment

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

Что с неймингом

{
if (RectangleHasInсorrectSize(r))
throw new ArgumentException($"Rectangle has incorrect size: width = {r.Width}, height = {r.Height}");
rectangles.Add(r);
}

public Rectangle? FindNearestByDirection(Rectangle r, Direction direction)
{
if (RectangleHasInсorrectSize(r))
throw new ArgumentException($"Rectangle has incorrect size: width= {r.Width}, height={r.Height}");
if (rectangles.Count == 0)
return null;
var calculator = GetMinDistanceCalculatorBy(direction);
return rectangles.Select(currentRectangle => (distance: calculator(currentRectangle, r), CurrentEl: currentRectangle))
.Where(el => el.distance > 0)
.MinBy(el => el.distance).CurrentEl;
}

private Func<Rectangle, Rectangle, int> GetMinDistanceCalculatorBy(Direction direction)
{
switch (direction)
{
case Direction.Left: return (possibleNearest, rectangleForFind) => rectangleForFind.X - possibleNearest.X;
case Direction.Right: return (possibleNearest, rectangleForFind) => possibleNearest.X - rectangleForFind.X;
case Direction.Top: return (possibleNearest, rectangleForFind) => rectangleForFind.Y - possibleNearest.Y;
default: return (possibleNearest, rectangleForFind) => possibleNearest.Y - rectangleForFind.Y;
}
}

private bool RectangleHasInсorrectSize(Rectangle r)
{
return r.Width <= 0 || r.Height <= 0;
}
}
}
193 changes: 193 additions & 0 deletions cs/TagsCloudVisualization/CircleLayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
using System.Drawing;

namespace TagsCloudVisualization;

public class CircleLayer
{
public enum Sector
{
Top_Right,
Bottom_Right,
Bottom_Left,
Top_Left
}

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


private Sector currentSector;
private readonly List<Rectangle> layerRectangles;
private CircleLayer prevLayer;

public CircleLayer(Point center, int radius)
{
Center = center;
Radius = radius;
currentSector = Sector.Top_Right;
layerRectangles = new List<Rectangle>();
}

public CircleLayer OnSuccessInsertRectangle(Rectangle inserted)
{
currentSector = currentSector == Sector.Top_Left ? Sector.Top_Right : currentSector + 1;
layerRectangles.Add(inserted);
if (currentSector == Sector.Top_Right)
return CreateNextLayer();
return this;
}

private CircleLayer CreateNextLayer()
{
var nextLayer = new CircleLayer(Center, CalculateRadiusForNextLayer());
nextLayer.prevLayer = this;
return nextLayer;
}

private int CalculateRadiusForNextLayer()
{
var prevSector = Sector.Top_Right - 1;
return layerRectangles.Select(r => CalculateDistanceBetweenCenterAndRectangleBySector(r, prevSector + 1)).Max();
}

private int CalculateDistanceBetweenCenterAndRectangleBySector(Rectangle r, Sector s)
{
switch (s)
{
case Sector.Top_Right:
return CalculateDistanceBetweenPoints(Center, new Point(r.X + r.Width, r.Y));
case Sector.Bottom_Right:
return CalculateDistanceBetweenPoints(Center, new Point(r.X + r.Width, r.Y + r.Height));
case Sector.Bottom_Left:
return CalculateDistanceBetweenPoints(Center, new Point(r.X - r.Width, r.Y + r.Height));
default:
return CalculateDistanceBetweenPoints(Center, new Point(r.X - r.Width, r.Y));
}
}

private int CalculateDistanceBetweenPoints(Point p1, Point p2)
{
return (int)Math.Ceiling(Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y)));
}

public Point CalculateTopLeftRectangleCornerPosition(Size rectangleSize)
{
var relevantForSectorPosition = GetPositionRelevantForSector();
switch (currentSector)
{
case Sector.Top_Right:
return new Point(relevantForSectorPosition.X, relevantForSectorPosition.Y - rectangleSize.Height);
case Sector.Bottom_Right:
return relevantForSectorPosition;
case Sector.Bottom_Left:
return new Point(relevantForSectorPosition.X - rectangleSize.Width, relevantForSectorPosition.Y);
default:
return new Point(relevantForSectorPosition.X - rectangleSize.Width,
relevantForSectorPosition.Y - rectangleSize.Height);
}
}

private Point GetPositionRelevantForSector()
{
switch (currentSector)
{
case Sector.Top_Right:
return new Point(Center.X, Center.Y - Radius);
case Sector.Bottom_Right:
return new Point(Center.X + Radius, Center.Y);
case Sector.Bottom_Left:
return new Point(Center.X, Center.Y + Radius);
default:
return new Point(Center.X - Radius, Center.Y);
}
}

public Point GetRectanglePositionWithoutIntersection(Rectangle forInsertion, Rectangle intersected)
{
return CalculateNewPositionWithoutIntersectionBySector(currentSector, forInsertion, intersected);
}

private Point CalculateNewPositionWithoutIntersectionBySector(Sector s, Rectangle forInsertion, Rectangle intersected)
{
var distanceForMoving = CalculateDistanceForMovingBySector(s, forInsertion, intersected);
var isMovingAxisIsX = IsMovingAxisIsXBySector(s);
var nearestForCenterCorner =
CalculateCornerNearestForCenterAfterMove(s, distanceForMoving, forInsertion);
var distanceForBringBackOnCircle =
CalculateDeltaForBringRectangleBackOnCircle(nearestForCenterCorner, isMovingAxisIsX);
distanceForMoving *= CalculateMoveMultiplierForMoveClockwise(isMovingAxisIsX, forInsertion);
distanceForBringBackOnCircle *= CalculateMoveMultiplierForMoveFromCenter(!isMovingAxisIsX, forInsertion);
return isMovingAxisIsX
? new Point(forInsertion.X + distanceForMoving, forInsertion.Y + distanceForBringBackOnCircle)
: new Point(forInsertion.X + distanceForBringBackOnCircle, forInsertion.Y + distanceForMoving);
}

private int CalculateDeltaForBringRectangleBackOnCircle(Point nearestForCenterCorner, bool isMovingAxisIsX)
{
Func<Point, int> getAxisForBringBackOnCircle = isMovingAxisIsX ? p => p.Y : p => p.X;
Func<Point, int> getStaticAxis = isMovingAxisIsX ? p => p.X : p => p.Y;

var distanceOnStaticAxis = Math.Abs(getStaticAxis(nearestForCenterCorner) - getStaticAxis(Center));
var distanceOnAxisForBringBackOnCircle = Math.Abs(getAxisForBringBackOnCircle(nearestForCenterCorner) - getAxisForBringBackOnCircle(Center));
return (int)Math.Ceiling(Math.Sqrt(Radius * Radius - distanceOnStaticAxis * distanceOnStaticAxis)) - distanceOnAxisForBringBackOnCircle;
}

private Point CalculateCornerNearestForCenterAfterMove(Sector s, int distanceForMoving, Rectangle r)
{
var isAxisForMoveIsX = IsMovingAxisIsXBySector(s);
var moveMultiplier = CalculateMoveMultiplierForMoveClockwise(isAxisForMoveIsX, r);
distanceForMoving *= moveMultiplier;
var nearestCorner = GetCornerNearestForCenterBySector(s, r);
return isAxisForMoveIsX
? new Point(nearestCorner.X + distanceForMoving, nearestCorner.Y)
: new Point(nearestCorner.X, nearestCorner.Y + distanceForMoving);
}

private int CalculateMoveMultiplierForMoveFromCenter(bool isAxisForMoveIsX, Rectangle r)
{
return isAxisForMoveIsX
? r.Right < Center.X ? -1 : 1
: r.Bottom < Center.Y ? -1 : 1;
}
private int CalculateMoveMultiplierForMoveClockwise(bool isAxisForMoveIsX, Rectangle r)
{
return isAxisForMoveIsX
? r.Left > Center.X ? -1 : 1
: r.Bottom > Center.Y ? -1 : 1;
}

private int CalculateDistanceForMovingBySector (Sector s, Rectangle forInsertion, Rectangle intersected)
{
switch (s)
{
case Sector.Top_Right:
return Math.Abs(forInsertion.Top - intersected.Bottom);
case Sector.Bottom_Right:
return Math.Abs(forInsertion.Right - intersected.Left);
case Sector.Bottom_Left:
return Math.Abs(forInsertion.Bottom - intersected.Top);
default:
return Math.Abs(forInsertion.Left - intersected.Right);
}
}

private Point GetCornerNearestForCenterBySector(Sector s, Rectangle r)
{
switch (s)
{
case Sector.Top_Right:
return new Point(r.Left, r.Bottom);
case Sector.Bottom_Right:
return new Point(r.Left, r.Top);
case Sector.Bottom_Left:
return new Point(r.Right, r.Top);
default:
return new Point(r.Right, r.Bottom);
}
}

private bool IsMovingAxisIsXBySector(Sector s)
{
return s == Sector.Bottom_Right || s == Sector.Top_Left;
}
}
80 changes: 80 additions & 0 deletions cs/TagsCloudVisualization/CircularCloudLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TagsCloudVisualization
{
public class CircularCloudLayouter
{
private List<Rectangle> rectanglesLocation = new ();
private readonly Point center;
private BruteForceNearestFinder nearestFinder;

public CircleLayer CurrentLayer { get; private set; }

public CircularCloudLayouter(Point center)
{
this.center = center;
nearestFinder = new BruteForceNearestFinder();
}
public Rectangle PutNextRectangle(Size rectangleSize)
{
Rectangle resultRectangle;
if (IsFirstRectangle())
{
CreateFirstLayer(rectangleSize);
resultRectangle = PutRectangleToCenter(rectangleSize);
}
else
{
var possiblePosition = CurrentLayer.CalculateTopLeftRectangleCornerPosition(rectangleSize);
resultRectangle = new Rectangle(possiblePosition, rectangleSize);
}
OnSuccessInsertion(resultRectangle);
return resultRectangle;
}

private void OnSuccessInsertion(Rectangle r)
{
rectanglesLocation.Add(r);
nearestFinder.Insert(r);
if (IsNotFirstInsertion())
CurrentLayer.OnSuccessInsertRectangle(r);
}

private void CreateFirstLayer(Size firstRectangle)
{
var radius = Math.Ceiling(Math.Max(firstRectangle.Width, firstRectangle.Height) / 2.0);
CurrentLayer = new CircleLayer(center, (int)radius);
}

private Rectangle PutRectangleToCenter(Size rectangleSize)
{
var rectangleX = center.X - rectangleSize.Width / 2;
var rectangleY = center.Y - rectangleSize.Height / 2;

return new Rectangle(new Point(rectangleX, rectangleY), rectangleSize);
}

private bool IsFirstRectangle()
{
return rectanglesLocation.Count == 0;
}

private bool IsNotFirstInsertion()
{
return rectanglesLocation.Count > 1;
}

public IEnumerable<Rectangle> GetRectangles()
{
foreach (var rectangle in rectanglesLocation)
{
yield return rectangle;
}
}
}
}
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
}
}
Loading