- Think of an unlimited grid with just a left edge.
- Each event is one cell wide, and the height and vertical position is fixed based on starting and ending times.
- Try to place each event in a column as far left as possible, without it intersecting any earlier event in that column.
- Then, when each connected group of events is placed, their actual widths will be 1/n of the maximum number of columns used by the group.
- You could also expand the events at the far left and right to use up any remaining space.
/// Pick the left and right positions of each event, such that there are no overlap.
/// Step 3 in the algorithm.
void LayoutEvents(IEnumerable<Event> events)
{
var columns = new List<List<Event>>();
DateTime? lastEventEnding = null;
foreach (var ev in events.OrderBy(ev => ev.Start).ThenBy(ev => ev.End))
{
if (ev.Start >= lastEventEnding)
{
PackEvents(columns);
columns.Clear();
lastEventEnding = null;
}
bool placed = false;
foreach (var col in columns)
{
if (!col.Last().CollidesWith(ev))
{
col.Add(ev);
placed = true;
break;
}
}
if (!placed)
{
columns.Add(new List<Event> { ev });
}
if (lastEventEnding == null || ev.End > lastEventEnding.Value)
{
lastEventEnding = ev.End;
}
}
if (columns.Count > 0)
{
PackEvents(columns);
}
}
/// Set the left and right positions for each event in the connected group.
/// Step 4 in the algorithm.
void PackEvents(List<List<Event>> columns)
{
float numColumns = columns.Count;
int iColumn = 0;
foreach (var col in columns)
{
foreach (var ev in col)
{
int colSpan = ExpandEvent(ev, iColumn, columns);
ev.Left = iColumn / numColumns;
ev.Right = (iColumn + colSpan) / numColumns;
}
iColumn++;
}
}
/// Checks how many columns the event can expand into, without colliding with
/// other events.
/// Step 5 in the algorithm.
int ExpandEvent(Event ev, int iColumn, List<List<Event>> columns)
{
int colSpan = 1;
foreach (var col in columns.Skip(iColumn + 1))
{
foreach (var ev1 in col)
{
if (ev1.CollidesWith(ev))
{
return colSpan;
}
}
colSpan++;
}
return colSpan;
}
Edit: Now sorts the events, instead of assuming they is sorted.
Edit2: Now expands the events to the right, if there are enough space.