javascript - 日历事件的可视化。以最大宽度布局事件的算法

标签 javascript algorithm calendar visualization

我需要你的算法帮助(它将在客户端使用 javascript 开发,但并不重要,我主要对算法本身感兴趣)布置日历事件以便每个事件框具有最大宽度.请看下图:

calendar events layout

Y 轴是时间。因此,如果“测试事件”在中午开始(例如)并且没有更多内容与其相交,则它占用整个 100% 的宽度。 “每周回顾”与“翻滚的基督教青年会”和“安娜/阿米莉亚”有交集,但后两者没有交集,所以都占了50%。 Test3、Test4 和 Test5 都相交,因此每个最大宽度为 33.3%。但是 Test7 是 66%,因为 Test3 是固定的 33%(见上文),所以它占用了所有可用空间,即 66%。

我需要一个算法来布置它。

提前致谢

最佳答案

  1. 想象一个只有左边的无限网格。
  2. 每个事件都是一个单元格宽,高度和垂直位置根据开始和结束时间固定。
  3. 尽量将每个事件放在尽可能靠左的列中,不要与该列中的任何较早的事件相交。
  4. 然后,当放置每个连接的事件组时,它们的实际宽度将为该组使用的最大列数的 1/n。
  5. 您还可以展开最左侧和最右侧的事件以用完所有剩余空间。
/// 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;
}

编辑:现在对事件进行排序,而不是假定它们已排序。

Edit2:如果有足够的空间,现在将事件扩展到右侧。

关于javascript - 日历事件的可视化。以最大宽度布局事件的算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11311410/

相关文章:

javascript - DataTables 将不是列的对象数据添加到行?

javascript - 无法为预出价槽复制 JS 变量

javascript - html5 Canvas 线宽空格

算法:最小路径交替颜色

javascript - HTML Doctype 设置/IE Quirks 模式

algorithm - 测量数据质量的技术和实践是什么?

判断两个数组是否具有相同成员的算法

个人记事本的 XML 结构

java - Android 日历 API 与事件

java - 如何在Java中将十进制时间戳转换为带有尾随小数的日期