Visualization of calendar events. Algorithm to layout events with maximum width

前端 未结 2 419
小蘑菇
小蘑菇 2020-12-04 07:38

I need your help with an algorithm (it will be developed on client side with javascript, but doesn\'t really matter, I\'m mostly interested in the algorithm itself) laying o

2条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-04 08:17

    1. Think of an unlimited grid with just a left edge.
    2. Each event is one cell wide, and the height and vertical position is fixed based on starting and ending times.
    3. Try to place each event in a column as far left as possible, without it intersecting any earlier event in that column.
    4. 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.
    5. 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 events)
    {
        var columns = new List>();
        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 { 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> 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> 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.

提交回复
热议问题