arrange static method

List<SideBySidePlacement> arrange(
  1. Iterable<VerticalLayoutData> verticalLayoutData
)

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;
}