using System.Collections.Generic;
namespace MyInfoMate.Vr.Menu
{
///
/// Placement « bento » dense sur une grille de colonnes — le socle du menu flottant.
///
/// Port C# de myinfomate_layout (Dart), qui sert au manager et à
/// mymuseum-visitapp ; le web refait le même placement en CSS Grid. Trois
/// implémentations, une seule règle : chaque item va à la première cellule libre,
/// parcours ligne par ligne, qui l'accueille — soit grid-auto-flow: dense.
///
/// Porté plutôt que calculé par le serveur : l'export reste une description du
/// contenu, pas une mise en page. Un placement calculé à distance ferait qu'un vieux
/// contenu en cache porterait une vieille mise en page.
///
public static class BentoLayout
{
public struct Item
{
public string Id;
public int ColSpan;
public int RowSpan;
public Item(string id, int colSpan = 1, int rowSpan = 1)
{
Id = id;
ColSpan = colSpan;
RowSpan = rowSpan;
}
}
public struct Placement
{
public string Id;
public int Col;
public int Row;
public int ColSpan;
public int RowSpan;
}
public struct Result
{
public List Placements;
public int RowCount;
}
public static Result Compute(IList- items, int columns)
{
var cols = columns < 1 ? 1 : columns;
var occupied = new List();
void EnsureRow(int r)
{
while (occupied.Count <= r) occupied.Add(new bool[cols]);
}
bool Fits(int r, int c, int cs, int rs)
{
if (c + cs > cols) return false;
for (var rr = r; rr < r + rs; rr++)
{
EnsureRow(rr);
for (var cc = c; cc < c + cs; cc++)
if (occupied[rr][cc]) return false;
}
return true;
}
void Fill(int r, int c, int cs, int rs)
{
for (var rr = r; rr < r + rs; rr++)
{
EnsureRow(rr);
for (var cc = c; cc < c + cs; cc++) occupied[rr][cc] = true;
}
}
var placements = new List();
foreach (var item in items)
{
var cs = item.ColSpan < 1 ? 1 : (item.ColSpan > cols ? cols : item.ColSpan);
var rs = item.RowSpan < 1 ? 1 : item.RowSpan;
var placed = false;
var r = 0;
while (!placed)
{
for (var c = 0; c + cs <= cols; c++)
{
if (!Fits(r, c, cs, rs)) continue;
Fill(r, c, cs, rs);
placements.Add(new Placement
{
Id = item.Id,
Col = c,
Row = r,
ColSpan = cs,
RowSpan = rs,
});
placed = true;
break;
}
if (!placed) r++;
}
}
var rowCount = occupied.Count;
while (rowCount > 0 && IsEmpty(occupied[rowCount - 1])) rowCount--;
return new Result { Placements = placements, RowCount = rowCount };
}
static bool IsEmpty(bool[] row)
{
foreach (var cell in row)
if (cell) return false;
return true;
}
}
}