arrange static method
Places verticalLayoutData in columns, so tiles that overlap never share horizontal space.
Tiles are taken by top, then by bottom. A tile that starts at or after the bottom of every tile before it starts a new group. Each tile goes into the first column free at its top and widens over the columns to its right that stay free for its whole height. A group has as many columns as it has tiles running at once.
Implementation
static List<SideBySidePlacement> arrange(Iterable<VerticalLayoutData> verticalLayoutData) {
final sorted = verticalLayoutData.toList()
..sort((a, b) {
final byTop = a.top.compareTo(b.top);
if (byTop != 0) return byTop;
final byBottom = b.bottom.compareTo(a.bottom);
if (byBottom != 0) return byBottom;
return a.id.compareTo(b.id);
});
final placements = <SideBySidePlacement>[];
final group = <(VerticalLayoutData, int)>[];
final columnBottoms = <double>[];
var groupBottom = double.negativeInfinity;
void closeGroup() {
final columns = columnBottoms.length;
for (final (data, column) in group) {
var span = 1;
while (column + span < columns && !group.any((other) => other.$2 == column + span && other.$1.overlaps(data))) {
span++;
}
placements.add(SideBySidePlacement(id: data.id, column: column, span: span, columns: columns));
}
group.clear();
columnBottoms.clear();
}
for (final data in sorted) {
if (group.isNotEmpty && data.top >= groupBottom) closeGroup();
var column = columnBottoms.indexWhere((bottom) => bottom <= data.top);
if (column == -1) {
column = columnBottoms.length;
columnBottoms.add(data.bottom);
} else {
columnBottoms[column] = data.bottom;
}
group.add((data, column));
groupBottom = group.length == 1 ? data.bottom : max(groupBottom, data.bottom);
}
if (group.isNotEmpty) closeGroup();
return placements;
}