Замощение плоскости произвольными прямоугольниками
У меня есть множество прямоугольных кусков(1-1, 2-1, 1-2, 2-2 и т.д.). Нужно их всех положить так, чтобы прямоугольную область, которую они занимают имела наименьшую площадь. Например у нас есть 1 кусок 1 на 1 и 1 2 на 1. Итого мы должны как то их скомпоновать на плоскости при наименьшей площади образуемого обрамляющего прямоугольника: прямоугольник получился 3 на 1, в начале на координате 0:0 стоит первый кусок, а на 1:0 второй.
Есть ли уже готовые алгоритмы такого замощения или придется думать самому? Если о применении, то это нужно для генерации общей текстуры из нескольких текстур
Ответы (1 шт):
Автор решения: Mical
→ Ссылка
что то получилось
static class Programm
{
public static void Main(string[] args)
{
Random random = new Random();
const int count = 15;
var texs = new Texture[count];
string views = "#!/+=.&^%$@()|5";
while (true)
{
Console.Clear();
var symb = 0;
for (int i = 0; i < count; i++)
{
texs[i] = new Texture(random.Next(1, 4), random.Next(1, 5), views[symb++]);
}
TextureAsm asembler = new TextureAsm(texs.ToList());
asembler.PlaceTextures();
Thread.Sleep(1000);
}
}
}
public struct TextureUnit(int x, int y, char view)
{
public int X = x, Y = y;
public char View = view;
public static TextureUnit operator +(TextureUnit tu1, (int xMove, int yMove) move)
{
return new TextureUnit { X = tu1.X + move.xMove, Y = tu1.Y + move.yMove, View = tu1.View };
}
public override string ToString()
{
return $"{X}x{Y} ({View})";
}
public override bool Equals(object obj)
{
if (obj is not TextureUnit)
return false;
var local = (TextureUnit)obj;
return local.X == X && local.Y == Y;
}
}
public class Texture
{
public int X, Y, XSize, YSize;
private List<TextureUnit> zeroUnits = new();
public List<TextureUnit> MovedUnits;
public Texture(int xSize, int ySize, char view)
{
XSize = xSize;
YSize = ySize;
for (int i = 0; i < XSize; i++)
{
for (int j = 0; j < YSize; j++)
{
zeroUnits.Add(new TextureUnit(i, j, view));
}
}
MovedUnits = zeroUnits;
}
public List<TextureUnit> GetUnits()
{
MovedUnits = zeroUnits.Select(i => i + (X, Y)).ToList();
return MovedUnits;
}
public int Square() => XSize * YSize;
public override int GetHashCode()
{
return 15;
}
public override string ToString()
{
return $"{XSize}x{YSize} > {X} \u2193 {Y}";
}
}
public class TextureAsm
{
public List<Texture> Textures;
public TextureAsm(List<Texture> textures)
{
Textures = textures;
}
public double PlaceTextures()
{
List<TextureUnit> outTexture = new List<TextureUnit>();
var sorted = Textures.OrderBy(i => i.Square()).Reverse().ToList();
foreach (var texture in sorted)
{
(int x, int y) currentSize = GetOutSize(outTexture);
(int x, int y) selectedPos = (0, 0);
int maxSq = int.MaxValue;
void trySet(int i, int j)
{
var tempTexture = new List<TextureUnit>(outTexture);
texture.X = i;
texture.Y = j;
var moved = texture.GetUnits();
bool next = false;
foreach (var unit in moved)
{
if (tempTexture.Contains(unit))
{
next = true;
break;
}
}
if (next)
return;
tempTexture.AddRange(moved);
var curSize = GetOutSize(tempTexture);
if (curSize.x * curSize.y >= maxSq)
return;
maxSq = curSize.x * curSize.y;
selectedPos = (i, j);
}
if (outTexture.Count != 0)
{
if (currentSize.x > currentSize.y)
for (int i = 0; i < currentSize.x + 1; i++)
{
for (int j = 0; j < currentSize.y + 1; j++)
{
trySet(i, j);
}
}
else
for (int j = 0; j < currentSize.y + 1; j++)
{
for (int i = 0; i < currentSize.x + 1; i++)
{
trySet(i, j);
}
}
}
texture.X = selectedPos.x;
texture.Y = selectedPos.y;
outTexture.AddRange(texture.GetUnits());
DrawSet(outTexture);
}
(int x, int y) finalSize = GetOutSize(outTexture);
Console.SetCursorPosition(0, finalSize.y + 1);
Console.WriteLine(finalSize);
Console.WriteLine(outTexture.Count);
Console.WriteLine($"{Math.Round(outTexture.Count / (double)(finalSize.y * finalSize.x) * 100, 2)} %");
return outTexture.Count / (double)(finalSize.y * finalSize.x) * 100;
}
(int x, int y) GetOutSize(List<TextureUnit> outTexture)
{
(int x, int y) res = (0, 0);
foreach (var unit in outTexture)
{
if (res.x < unit.X)
res.x = unit.X;
if (res.y < unit.Y)
res.y = unit.Y;
}
return (res.x + 1, res.y + 1);
}
static void DrawSet(List<TextureUnit> set)
{
var cursor = Console.GetCursorPosition();
foreach (var unit in set)
{
Console.SetCursorPosition(unit.X, unit.Y);
Console.Write(unit.View);
}
Console.SetCursorPosition(cursor.Left, cursor.Top);
}
}
в итоге +- оптимальностью получается 93-94%