mirror of
https://github.com/sinbad/StevesUEHelpers.git
synced 2025-02-23 17:45:23 +00:00
Add Fill2DRegionWithRectangles function
This commit is contained in:
parent
0ccd90cdd8
commit
df40ca92e5
@ -106,3 +106,128 @@ float StevesMathHelpers::GetDistanceToConvex2D(const TArray<FVector2f>& ConvexPo
|
|||||||
|
|
||||||
return bInside ? -ClosestInside : ClosestOutside;
|
return bInside ? -ClosestInside : ClosestOutside;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int StevesMathHelpers::Fill2DRegionWithRectangles(int StartX,
|
||||||
|
int StartY,
|
||||||
|
int Width,
|
||||||
|
int Height,
|
||||||
|
std::function<bool(int, int)> CellIncludeFunc,
|
||||||
|
TArray<FIntRect>& OutRects)
|
||||||
|
{
|
||||||
|
int RectCount = 0;
|
||||||
|
|
||||||
|
const int Len = Width*Height;
|
||||||
|
TArray<bool> bDoneMarkers;
|
||||||
|
bDoneMarkers.SetNumUninitialized(Len);
|
||||||
|
int CellsTodo = 0;
|
||||||
|
|
||||||
|
int StartN = 0;
|
||||||
|
const int EndX = StartX + Width - 1;
|
||||||
|
const int EndY = StartY + Height - 1;
|
||||||
|
// Initialise done markers based on func
|
||||||
|
for (int y = 0; y < Height; ++y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < Width; ++x)
|
||||||
|
{
|
||||||
|
const bool Include = CellIncludeFunc(x+StartX, y+StartY);
|
||||||
|
bDoneMarkers[y*Width + x] = !Include;
|
||||||
|
if (Include)
|
||||||
|
{
|
||||||
|
if (++CellsTodo == 1)
|
||||||
|
{
|
||||||
|
// This is the one we'll start with, might as well calculate it while we're here
|
||||||
|
StartN = y*Width + x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (CellsTodo > 0)
|
||||||
|
{
|
||||||
|
// Find next starting point, from last one, until not done
|
||||||
|
for (; StartN < Len && bDoneMarkers[StartN]; ++StartN) {}
|
||||||
|
|
||||||
|
// Shouldn't happen, but just in case
|
||||||
|
if (StartN >= Len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// NOTE: this X/Y is local (based at 0,0 not StartX/StartY, for use with DoneMarkers)
|
||||||
|
const int LocalStartX = StartN % Width;
|
||||||
|
const int LocalStartY = StartN / Width;
|
||||||
|
|
||||||
|
// We try not to create long & thin rects if we can help it, unlike greedy meshing
|
||||||
|
// We're greedy in alternate dims to try to make fatter quads
|
||||||
|
bool bCanExtendX = true, bCanExtendY = true;
|
||||||
|
bool bExtendingX = true;
|
||||||
|
int W = 1;
|
||||||
|
int H = 1;
|
||||||
|
|
||||||
|
while (bCanExtendX || bCanExtendY)
|
||||||
|
{
|
||||||
|
// Try extending right
|
||||||
|
if (bExtendingX)
|
||||||
|
{
|
||||||
|
bool ExtendOK = LocalStartX+W < Width;
|
||||||
|
for (int TestY = LocalStartY; ExtendOK && TestY < LocalStartY+H; ++TestY)
|
||||||
|
{
|
||||||
|
if (bDoneMarkers[TestY*Width + LocalStartX+W])
|
||||||
|
{
|
||||||
|
// No good
|
||||||
|
ExtendOK = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ExtendOK)
|
||||||
|
{
|
||||||
|
++W;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bCanExtendX = false;
|
||||||
|
}
|
||||||
|
// Flip extending axis if possible
|
||||||
|
if (bCanExtendY)
|
||||||
|
bExtendingX = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try extending down
|
||||||
|
bool ExtendOK = LocalStartY+H < Height;
|
||||||
|
for (int TestX = LocalStartX; ExtendOK && TestX < LocalStartX+W; ++TestX)
|
||||||
|
{
|
||||||
|
if (bDoneMarkers[(LocalStartY+H)*Width + TestX])
|
||||||
|
{
|
||||||
|
// No good
|
||||||
|
ExtendOK = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ExtendOK)
|
||||||
|
{
|
||||||
|
++H;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bCanExtendY = false;
|
||||||
|
}
|
||||||
|
// Flip extending axis if possible
|
||||||
|
if (bCanExtendX)
|
||||||
|
bExtendingX = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've calculated the max extension
|
||||||
|
for (int y = LocalStartY; y < LocalStartY+H; ++y)
|
||||||
|
{
|
||||||
|
for (int x = LocalStartX; x < LocalStartX+W; ++x)
|
||||||
|
{
|
||||||
|
bDoneMarkers[y*Width+x] = true;
|
||||||
|
--CellsTodo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutRects.Add(FIntRect(StartX+LocalStartX, StartY+LocalStartY, StartX+LocalStartX+W-1, StartY+LocalStartY+H-1));
|
||||||
|
++RectCount;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return RectCount;
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
struct FKConvexElem;
|
struct FKConvexElem;
|
||||||
|
|
||||||
@ -147,4 +148,32 @@ public:
|
|||||||
const float d = (v2.X - v1.X) * (p.Y - v1.Y) - (v2.Y - v1.Y) * (p.X - v1.X);
|
const float d = (v2.X - v1.X) * (p.Y - v1.Y) - (v2.Y - v1.Y) * (p.X - v1.X);
|
||||||
return d == 0 || (d < 0) == (s + t <= 0);
|
return d == 0 || (d < 0) == (s + t <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that tries to fill a 2D area with the largest rectangles it can. The area is abstractly defined as a boundary
|
||||||
|
* index area with start X/Y and width/height, and will call back the CellIncludeFunc to determine whether a given
|
||||||
|
* cell index X/Y should be considered valid to include in a rectangle. This means you can define irregular grids of
|
||||||
|
* "valid" cells, and this function will fill the area with the largest rectangles it can while staying out of "invalid"
|
||||||
|
* cells.
|
||||||
|
* If you return "true" from every call to your CellIncludeFunc then the result will be a single rectangle covering
|
||||||
|
* the entire area. It's expected that you will return "false" for some X/Y combinations and that will cause the area
|
||||||
|
* to be split into multiple rectangles.
|
||||||
|
* The returned rectangles will not overlap, and the entire valid area will be filled.
|
||||||
|
* @param StartX The start X index. This is defined by your own data, so you can address a subset if you want.
|
||||||
|
* @param StartY The start Y index.This is defined by your own data, so you can address a subset if you want.
|
||||||
|
* @param Width The width of the area to fill. This is defined by your own data, so you can address a subset if you want.
|
||||||
|
* @param Height The height of the area to fill. This is defined by your own data, so you can address a subset if you want.
|
||||||
|
* @param CellIncludeFunc Your function which given an X/Y cell index, must return true if that cell is valid to be
|
||||||
|
* included in a rectangle.
|
||||||
|
* @param OutRects Array of rectangles which this function should append results to. Will not be cleared before adding.
|
||||||
|
* @return The number of rectangles added by this call. Each rectangle is a min/max inclusive X/Y value.
|
||||||
|
*/
|
||||||
|
static int Fill2DRegionWithRectangles(int StartX,
|
||||||
|
int StartY,
|
||||||
|
int Width,
|
||||||
|
int Height,
|
||||||
|
std::function<bool(int, int)> CellIncludeFunc,
|
||||||
|
TArray<FIntRect>& OutRects);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user