从消耗时间的项目和不消耗时间但仍需要绘制空间的项目生成线性的、基于时间线的表示

发布于 2024-12-20 08:08:15 字数 4093 浏览 0 评论 0原文

这是一个关于为一组并行数据生成图像或任何其他表示的问题。它不是关于绘图或 GUI 编程,而是计算位置。 首先,我将解释一下我现在的立场,第二张图片和示例显示了我的问题。

当前状态

exampleOne-Easy http://www.wargsang.de/text3935.png

我有一维对象,但它们通过将它们放置在平行“线”上来对齐。我们将这种一维对象称为“事件”,其以“持续时间”作为时间单位。 这些事件有一个变体,即什么也没有发生,没有数据但有持续时间的对象;一个“间隙”对象。

因此,我们得到了一个时间表,其中包含由事件和间隙组成的同时对象,这很容易作为三个对象列表来处理。 可视化也很简单:循环列表并根据其持续时间绘制每个对象。

class Event():
    def __init__(self, duration, displacement = 0):  #displacement is explained in the second example and the core problem of this question
        self.duration = duration
        self.displacement = displacement
        #additional data

    def self.draw(self, start_coordinate):
        """draw duration * 10 pixels in black"""
        #drawing code using start_coordinate to place the drawn object. see example graphic
        return duration * 10


class Gap():
    def __init__(self, duration, displacement = 0):
        self.duration = duration
        self.displacement = displacement
        #no data

    def self.draw(self, start_coordinate):
        """draw duration * 10 pixels in transparent"""
        #drawing code using start_coordinate to place the drawn object. see example graphic
        return duration * 10

row_one = [Event(1), Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(2)]
row_two = [Event(1), Gap(2), Event(1), Event(1), Gap(1), Event(1), Gap(1), ]
row_thr = [Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(3),]

timetable = [row_one, row_two, row_thr]

for row in timetable:
    pixelcounter = 0 # the current position.
    for item in row:
        ret = item.draw(pixelcounter) #draw on the current position. Get how width the item was
        pixelcounter += ret #save width for the next iteration        
    #instructions to move the "drawing cursor" down a few pixels so the next row does not overlap.     

问题

现在来解决问题。有些对象需要图形空间但持续时间为零。 我称之为“位移”。

exampleTwo-Problematic http://www.wargsang.de/text4120.png

或者我们需要有持续时间但也有位移。 当我们只有一行时,这仍然不是问题,但同步行更加复杂,我没有解决方案。

在上图中,红色块的持续时间为零并且已移位。蓝色块有持续时间并且也会移位。

示例: *想象一个会议的时间表,每小时都有不同的发言者时段(我们的持续时间时段)。每行代表一个不同的会议室。

  • 黑色块是演讲,其中可能写有一个简短的主题(以图形方式)。

  • 蓝色块也是演讲,但是主题太长写不出来,所以我们需要更多的空间。

  • 红色是诸如房间号变更之类的注释。它们不占用自己的时间,而是与它们之后的所有项目相关。*

任务是找到一种方法从上面的函数计算像素计数器,以便它对于每一行都是正确的,而且对于一行中的位移也是正确的row 影响所有其他行并在那里创建额外的空间。 目标是每行中的持续时间是固定的和对齐的。任何应该从单位计数 4 开始的事件或间隙都应该从相同的绝对位置开始。

这意味着任何零持续时间/位移对象都从实际时间/持续时间点开始,但本身不消耗任何时间/持续时间,以便所有后续项目以完全相同的持续时间开始,直到包含下一个实际持续时间事件。从另一个角度来看,这也意味着零持续时间项目始终在具有持续时间的事件之前开始。

在图中,我们可以在第 2 列中看到一个相当简单的情况,这也意味着这将开始第二个持续时间段。尽管该列中存在三个真实事件,但由于存在替换项而被向右移动。 第 4 列有一个持续时间项目,它也有位移。同样,从槽 5 开始的所有项目都会向右移动。 第6栏是最有趣的也是我真正的问题,我在这里找不到解决方案。同样,第 6 列中的所有真实事件都向右移动,但仍然同时开始。但这里我们有 a) 两行位移对象和两个 b) 两个对象紧随其后。 因此,对于真实事件来说,了解完整的位移很重要,但对于第三行中的第二个对象来说,了解在它之前还有一个位移项也很重要。

警告:图形表示可能建议采用基于表格的方法,其中每列都有单独的宽度。但这就是这个例子的结束。实际应用程序处理每个事件的常见持续时间为 300-10,000,但持续时间为 1 的可能性不大,但在技术上是可能的。因此该表的列宽为一个持续时间。考虑到我们进入了数十万个完整持续时间(乘以行数),这可能会降低性能。

这张图片的数据看起来像这样。如何用这些数据绘制第二张图像?或者需要改变什么,我愿意接受所有建议。

非常感谢您的时间和兴趣。如果您不知道解决方案,请随时向我提问或向我展示我的概念的缺陷,这也会帮助我思考。

row_one = [ Event(1), #1
            Event(0,1), Event(1), #2
            Gap(1), #3
            Event(1), #4
            Gap(1), #5
            Event(0,1), Event(1), #6
            Event(1), #7
            ] 
row_two = [ Event(1), #1
            Event(1), #2
            Gap(1), #3
            Event(1, 0.5), #4, 0,5 is not important. we can also simply to just ints. The important bit is that it has both values.
            Event(1), #5
            Event(1), #6
            Event(1), #7
            ] 
row_thr = [ Event(1), #1
            Event(1), #2
            Event(1), #3            
            Event(1), #4
            Event(1), #5            
            Event(0,1), Event(0,1), Event(1), #6   #Please pay attention to this case.
            Event(1), #7
            ] 

This is a question about generating an image, or any other representations, for a set of parallel data. Is is not about drawing or GUI programming but calculating positions.
First I'll explain a bit where I stand right now and the second image and example shows my problem.

Current Status

exampleOne-Easy http://www.wargsang.de/text3935.png

I have one-dimensional objects but they are aligned by placing them on parallel "lines". Let's call this one-dimensional objects "events" which have a "Duration" as a unit of time.
These events have a variant where nothing happens, objects with no data but a duration; A "gap"-object.

So we get a timetable with simulatanious objects consisting of events and gaps, which is pretty easy to handle as three lists of objects.
The visualization is simple as well: Loop over the lists and draw each object according to its duration.

class Event():
    def __init__(self, duration, displacement = 0):  #displacement is explained in the second example and the core problem of this question
        self.duration = duration
        self.displacement = displacement
        #additional data

    def self.draw(self, start_coordinate):
        """draw duration * 10 pixels in black"""
        #drawing code using start_coordinate to place the drawn object. see example graphic
        return duration * 10


class Gap():
    def __init__(self, duration, displacement = 0):
        self.duration = duration
        self.displacement = displacement
        #no data

    def self.draw(self, start_coordinate):
        """draw duration * 10 pixels in transparent"""
        #drawing code using start_coordinate to place the drawn object. see example graphic
        return duration * 10

row_one = [Event(1), Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(2)]
row_two = [Event(1), Gap(2), Event(1), Event(1), Gap(1), Event(1), Gap(1), ]
row_thr = [Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(3),]

timetable = [row_one, row_two, row_thr]

for row in timetable:
    pixelcounter = 0 # the current position.
    for item in row:
        ret = item.draw(pixelcounter) #draw on the current position. Get how width the item was
        pixelcounter += ret #save width for the next iteration        
    #instructions to move the "drawing cursor" down a few pixels so the next row does not overlap.     

The Problem

Now to the problem. There are objects which need a graphical space but have zero duration.
I call this "Displacement".

exampleTwo-Problematic http://www.wargsang.de/text4120.png

Or we need objects which have duration but also have displacement.
This is still not a problem when we only have a single row, but synchronizing the rows is more complex and I don't have a solution for this.

In the picture above the red blocks have zero duration and are displaced. Blue blocks have duration and are displaced, too.

Example:
*Imagine a timetable for a conference with different speaker slots each hour (our duration-slots). Each row represents a different conference-room.

  • The black blocks are speeches and maybe have a short topic written within them (graphically).

  • Blue blocks are speeches, too, but the topic was too long to write so we need more space once.

  • Red are notes like a room-number change. They don't take time of their own but relate to all items which come after them.*

The task is to find a way to calulate the pixelcounter from the function above so that it is correct for each row alone but also that displacement in one row affects all other rows and creates additional space there.
The goal is that durations are fixed and aligned in each row. Any event or gap that should start on, for example, unit-count 4, should begin at the same absolute position.

This means that any zero-duration/displacement object starts at a real point in time/duration but does not consume any time/duration itself so that all subsequent items start on the very same duration until the next real duration event is included. From another point of view that also means that zero-duration items start always before the events which have duration.

In the picture we can see a rather simple case in column 2, which also means this starts the second duration slot. Eventhough there are three real events in that column that are shifted to the right because a displace item is there.
Column 4 has a duration item which has displacement as well. Again, All items that start in slot 5 are shifted to the right.
Colum 6 is the most interesting and my real problem, I can't find a solution here. Again, all real events in column 6 are shifted to the right and still start at the same time. But here we have a)Displacement-Objects in two rows and two b) two objects right behind each other.
So it is important for the real events to know the complete displacement but also important for the second object in the third row to know that there is one more displacement item before it.

Warning: The graphical representation may suggest a table-based approach where each column has an individual width. But this is where this example ends. The real application deals with common duration of 300-10,000 per event but durations of 1 are unlikely but technically possible. So the table would have a column-width of one duration. Considering we got into the hundred thousands of complete duration (times the number of rows) this may drag down the performance.

The data of this picture would look like this. How can I draw the second image with this data? Or what needs to be changed, I am open for all suggestions.

Thank you very much for your time and interest. If you don't know a solution please don't hesitate to ask me questions or show me flaws of my concept, it will help me think as well.

row_one = [ Event(1), #1
            Event(0,1), Event(1), #2
            Gap(1), #3
            Event(1), #4
            Gap(1), #5
            Event(0,1), Event(1), #6
            Event(1), #7
            ] 
row_two = [ Event(1), #1
            Event(1), #2
            Gap(1), #3
            Event(1, 0.5), #4, 0,5 is not important. we can also simply to just ints. The important bit is that it has both values.
            Event(1), #5
            Event(1), #6
            Event(1), #7
            ] 
row_thr = [ Event(1), #1
            Event(1), #2
            Event(1), #3            
            Event(1), #4
            Event(1), #5            
            Event(0,1), Event(0,1), Event(1), #6   #Please pay attention to this case.
            Event(1), #7
            ] 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

注定孤独终老 2024-12-27 08:08:15

我不完全确定,但我认为你想要的是:

  • 具有相同开始时间的不同行上的位移是同步的,即具有相同的水平位置(每行上的第一个位移是同步的)与其他行上的第一个位移)
  • 具有相同开始时间的“真实”事件和间隙是同步的,并且在具有相同开始时间的位移之后发生
  • 事件的宽度取决于其持续时间(以及可能的位移),但是其他行上的位移,即结束时间不同步

如果您希望结束时间同步,您必须解释如何同步;但我看不出有什么明显的办法。

然后,您将得到原始问题的以下解决方案(大写字母=事件,小写字母=位移,点=间隙,空格=“等待同步”,数字是事件的开始时间):

0 123 4  567
AbC.D .e FG
A B.CcD  EF
A BCD EfgHI

您可以看到在下面的例子中,结束时间不同步:

0  12
AAa
Aa B
AaaB

还有一个更大的随机例子:

                         11    11  1 1     1     1  1   1  22 22   2     2  22  22   33    333    3  3  3 3   3 4   44444  4   4   4  45
    01 2 34   5678    9  01    23  4 5     6     7  8   9  01 23   4     5  67  89   01    234    5  6  7 8   9 0   12345  6   7   8  90
    AAAA  BB   CCC  dd..  EEe   Fff..      GGGGGg           ....         ...    HHH   ....        IIii  JJJ     ...   KKK  LLLLl
abbbCCC  DDDDDdd ..      EEEEE       Fff   GGG          HHH   IIIii      JJJjj  KKKK       LLLl   Mno.  PPP    qR   SSSSSs TT   uuuVV
    ...  AAAAA   BBB      CC    DDDD             ...       EE FFFF          GHhhIIII       JJ.    K  Lll.m....       NNNO  ....
    ......     AAAA      ..    ....        BBB          CCCCCc     DDDDDd        Ee  FFFFff  G hhhIIIII         JJJ   KLLLLLll        M
    .. AAA    BBBCcc  DD  EE    ..   FFF           gH   IIIIIi     J     KKk LL  MMMMM       NNNNNn           OOo   PPQQQQ  rrr...
    AAAAa .   BBBBbb  CCCCC        DDDDDd            eeeFFFFF      GG       HH  .....       IIIII         JJ    K   LM.NNNNN          .
    AAAaaBBB   CCCcc  DDDDDdd      EeFF          ...       GGgHHHH          III  JJJJ       KKK    llMMMm  nnnOOOO    PPPp ...        Q
    AAAAA     BBBBB      CCCC      .....                DDD   EEEEE          FFFff   ....    GGGG         HHHHhh     II....     j  .  .
    AAAaa..   BBBBbb  CccDDDDD       ....               EEE   .F   GgghhhII  Jj KKKK       ...    ...     LLll  ...   MMMM     N   OooP
    ....  Aa  ..BCCC      .....            DDD          EEEe  FFf  .....         GGGG       HIIIIIii          . JJ   ....  KKk     LL
    AAAAAa  bbC.....      DDDDD            ....          eeFFFFff  GGGGG         ...    hh IIJJJ        KKK     L   MMMMMmmNNNN
    ..aBBB    CCCCc   .....        .....                ...   D.   E     FFFFFff   ggHHhiiiJKKKk     LLLLL       mmmNNNOP  Q   RRR
    AA BbCCCC   DD    Ee FFFFFff     GGGGG                 HH IIIi       JjjK..  LLLll     MMMMmm    ....       .   NNNOOOOOoo        P
    AB CCCCC    .....        ddEEEE     fffGgg   HHHHHhh      II jjKKKK         LLLL       MMMM    nn..   OO    PPPPPpp QQQQQqq
    AAA  BBB   CCCC      DDdd  EE  FFF        gggHh IIIii   JJJJ         K  LLLLl    MMm   NNOOOO         .   PP    .QQQRRRRR

现在是代码(抱歉太长了,看看 Timetable.__init__ 有趣的部分,其余的大部分都是漂亮的打印)。

from heapq import merge
from itertools import groupby, cycle, chain
from collections import defaultdict
from operator import attrgetter
from string import ascii_uppercase

# events are processed in this order:
# increasing start time, displacements (duration=0) first, and grouped by row_id
ev_sort_attrs = attrgetter("time", "duration", "row_id")


class Event:

    def __init__(self, duration, displacement=0, visible=True, time=None, row_id=None):
        self.duration = duration
        self.displacement = displacement
        self.visible = visible
        self.time = time
        self.row_id = row_id
        self.pos = None

    def draw(self, char):
        return char * self.duration + char.lower() * self.displacement

    def __lt__(self, other):
        return ev_sort_attrs(self) < ev_sort_attrs(other)


def Gap(duration):
    return Event(duration, visible=False)


class Timetable(list):
    def __init__(self, *args):
        """ compute positions for a list of rows of events """
        list.__init__(self, *args)

        # compute times for the events, and give them row_ids
        for i, row in enumerate(self):
            t = 0
            for ev in row:
                ev.time = t
                t += ev.duration
                ev.row_id = i

        # map times to position for displacements and event
        t2pos_disp = defaultdict(int) # maps times to position of synchronized start of displacements
        t2pos_ev = defaultdict(int) # maps times to position of synchronized start of events and gaps

        # the real work is done in the following loop
        t_prev = 0
        for t, g in groupby(merge(*self), key=attrgetter("time")):

            # different times should have a minimum distance corresponding to their difference
            t2pos_ev[t] = t2pos_disp[t] = max(t2pos_ev[t], t2pos_ev[t_prev] + t - t_prev)
            t_prev = t

            for (duration, row_id), g_row in groupby(g, key=attrgetter("duration", "row_id")): # process all displacements first, then the events
                pos_ev = t2pos_ev[t] if duration > 0 else t2pos_disp[t] # events and displacements start at different
                for ev in g_row:
                    ev.pos = pos_ev
                    pos_ev += ev.duration + ev.displacement
                t2pos_ev[t + ev.duration] = max(t2pos_ev[t + ev.duration], pos_ev)

        # keep our results...
        self.t2pos_ev = t2pos_ev
        self.t2pos_disp = t2pos_disp


    @staticmethod
    def str_row(row):
        """ draw row, uppercase letters for real events, lower case letters for
        displacements, dots for gaps"""

        ev_chars = cycle(ascii_uppercase)
        out = []
        l = 0
        for ev in row:
            if ev.pos > l:
                out.append(" " * (ev.pos - l))
            out.append(ev.draw(next(ev_chars) if ev.visible else "."))
            l = ev.pos + len(out[-1])
        return "".join(out)

    def __str__(self):
        max_t, max_p = max(self.t2pos_ev.items())
        w = len(str(max_t))
        header_temp = [" " * w] * (max_p + 1)
        for t, p in self.t2pos_ev.items():
            header_temp[p] = "%*d" % (w, t)
        headers = ("".join(header) for header in zip(*header_temp))

        rows = (self.str_row(row) for row in self)

        return "\n".join(chain(headers, rows))


if __name__ == "__main__":
    # original example
    row_one = [Event(1), Event(0,1), Event(1), Gap(1), Event(1), Gap(1), Event(0,1), Event(1), Event(1)]
    row_two = [Event(1), Event(1), Gap(1), Event(1, 1), Event(1), Event(1), Event(1)]
    row_thr = [Event(1), Event(1), Event(1), Event(1), Event(1), Event(0,1), Event(0,1), Event(1), Event(1)]

    timetable = Timetable([row_one, row_two, row_thr])
    print(timetable)

    print("-" * 80)

    # short example, shows ending times are not synchronized
    print(Timetable([[Event(2, 1)], [Event(1, 1), Event(1)], [Event(1, 2), Event(1)]]))

    print("-" * 80)

    # larger random example
    def random_row(l):
        import random
        res = []
        t = 0
        while t < l:
            x = random.random()
            if x < 0.1: res.append(Event(0, random.randint(1, 3)))
            elif x < 0.8: res.append(Event(min(random.randint(1, 5), l - t), random.randint(0, 1) * random.randint(0, 2)))
            else: res.append(Gap(min(random.randint(1, 5), l - t)))
            t += res[-1].duration
        return res

    print(Timetable([random_row(50) for _ in range(15)]))

I'm not completely sure, but I think what you want is:

  • displacements on different rows that have the same starting time are synchronized, i.e. have the same horizontal position (the first displacement on each row is synchronized with the first displacements on other rows)
  • "real" events and gaps that have the same starting time are synchronized, and come after the displacements with the same starting time
  • the width of an event depends on its duration (and possibly its displacement), but not on displacements on other rows, i.e. the ending times are not synchronized

If you want ending times to be synchronized, you'd have to explain how; but I see no obvious way.

Then you get the following solution for your original problem (capitals = events, lowercase letters = displacements, dots = gaps, space = "waiting for synchronization", numbers are starting times of events):

0 123 4  567
AbC.D .e FG
A B.CcD  EF
A BCD EfgHI

You can see that ending times are not synchronized in the following example:

0  12
AAa
Aa B
AaaB

And a larger random example:

                         11    11  1 1     1     1  1   1  22 22   2     2  22  22   33    333    3  3  3 3   3 4   44444  4   4   4  45
    01 2 34   5678    9  01    23  4 5     6     7  8   9  01 23   4     5  67  89   01    234    5  6  7 8   9 0   12345  6   7   8  90
    AAAA  BB   CCC  dd..  EEe   Fff..      GGGGGg           ....         ...    HHH   ....        IIii  JJJ     ...   KKK  LLLLl
abbbCCC  DDDDDdd ..      EEEEE       Fff   GGG          HHH   IIIii      JJJjj  KKKK       LLLl   Mno.  PPP    qR   SSSSSs TT   uuuVV
    ...  AAAAA   BBB      CC    DDDD             ...       EE FFFF          GHhhIIII       JJ.    K  Lll.m....       NNNO  ....
    ......     AAAA      ..    ....        BBB          CCCCCc     DDDDDd        Ee  FFFFff  G hhhIIIII         JJJ   KLLLLLll        M
    .. AAA    BBBCcc  DD  EE    ..   FFF           gH   IIIIIi     J     KKk LL  MMMMM       NNNNNn           OOo   PPQQQQ  rrr...
    AAAAa .   BBBBbb  CCCCC        DDDDDd            eeeFFFFF      GG       HH  .....       IIIII         JJ    K   LM.NNNNN          .
    AAAaaBBB   CCCcc  DDDDDdd      EeFF          ...       GGgHHHH          III  JJJJ       KKK    llMMMm  nnnOOOO    PPPp ...        Q
    AAAAA     BBBBB      CCCC      .....                DDD   EEEEE          FFFff   ....    GGGG         HHHHhh     II....     j  .  .
    AAAaa..   BBBBbb  CccDDDDD       ....               EEE   .F   GgghhhII  Jj KKKK       ...    ...     LLll  ...   MMMM     N   OooP
    ....  Aa  ..BCCC      .....            DDD          EEEe  FFf  .....         GGGG       HIIIIIii          . JJ   ....  KKk     LL
    AAAAAa  bbC.....      DDDDD            ....          eeFFFFff  GGGGG         ...    hh IIJJJ        KKK     L   MMMMMmmNNNN
    ..aBBB    CCCCc   .....        .....                ...   D.   E     FFFFFff   ggHHhiiiJKKKk     LLLLL       mmmNNNOP  Q   RRR
    AA BbCCCC   DD    Ee FFFFFff     GGGGG                 HH IIIi       JjjK..  LLLll     MMMMmm    ....       .   NNNOOOOOoo        P
    AB CCCCC    .....        ddEEEE     fffGgg   HHHHHhh      II jjKKKK         LLLL       MMMM    nn..   OO    PPPPPpp QQQQQqq
    AAA  BBB   CCCC      DDdd  EE  FFF        gggHh IIIii   JJJJ         K  LLLLl    MMm   NNOOOO         .   PP    .QQQRRRRR

And now the code (sorry it's so long, look at Timetable.__init__ for the interesting part, the rest is mostly pretty printing).

from heapq import merge
from itertools import groupby, cycle, chain
from collections import defaultdict
from operator import attrgetter
from string import ascii_uppercase

# events are processed in this order:
# increasing start time, displacements (duration=0) first, and grouped by row_id
ev_sort_attrs = attrgetter("time", "duration", "row_id")


class Event:

    def __init__(self, duration, displacement=0, visible=True, time=None, row_id=None):
        self.duration = duration
        self.displacement = displacement
        self.visible = visible
        self.time = time
        self.row_id = row_id
        self.pos = None

    def draw(self, char):
        return char * self.duration + char.lower() * self.displacement

    def __lt__(self, other):
        return ev_sort_attrs(self) < ev_sort_attrs(other)


def Gap(duration):
    return Event(duration, visible=False)


class Timetable(list):
    def __init__(self, *args):
        """ compute positions for a list of rows of events """
        list.__init__(self, *args)

        # compute times for the events, and give them row_ids
        for i, row in enumerate(self):
            t = 0
            for ev in row:
                ev.time = t
                t += ev.duration
                ev.row_id = i

        # map times to position for displacements and event
        t2pos_disp = defaultdict(int) # maps times to position of synchronized start of displacements
        t2pos_ev = defaultdict(int) # maps times to position of synchronized start of events and gaps

        # the real work is done in the following loop
        t_prev = 0
        for t, g in groupby(merge(*self), key=attrgetter("time")):

            # different times should have a minimum distance corresponding to their difference
            t2pos_ev[t] = t2pos_disp[t] = max(t2pos_ev[t], t2pos_ev[t_prev] + t - t_prev)
            t_prev = t

            for (duration, row_id), g_row in groupby(g, key=attrgetter("duration", "row_id")): # process all displacements first, then the events
                pos_ev = t2pos_ev[t] if duration > 0 else t2pos_disp[t] # events and displacements start at different
                for ev in g_row:
                    ev.pos = pos_ev
                    pos_ev += ev.duration + ev.displacement
                t2pos_ev[t + ev.duration] = max(t2pos_ev[t + ev.duration], pos_ev)

        # keep our results...
        self.t2pos_ev = t2pos_ev
        self.t2pos_disp = t2pos_disp


    @staticmethod
    def str_row(row):
        """ draw row, uppercase letters for real events, lower case letters for
        displacements, dots for gaps"""

        ev_chars = cycle(ascii_uppercase)
        out = []
        l = 0
        for ev in row:
            if ev.pos > l:
                out.append(" " * (ev.pos - l))
            out.append(ev.draw(next(ev_chars) if ev.visible else "."))
            l = ev.pos + len(out[-1])
        return "".join(out)

    def __str__(self):
        max_t, max_p = max(self.t2pos_ev.items())
        w = len(str(max_t))
        header_temp = [" " * w] * (max_p + 1)
        for t, p in self.t2pos_ev.items():
            header_temp[p] = "%*d" % (w, t)
        headers = ("".join(header) for header in zip(*header_temp))

        rows = (self.str_row(row) for row in self)

        return "\n".join(chain(headers, rows))


if __name__ == "__main__":
    # original example
    row_one = [Event(1), Event(0,1), Event(1), Gap(1), Event(1), Gap(1), Event(0,1), Event(1), Event(1)]
    row_two = [Event(1), Event(1), Gap(1), Event(1, 1), Event(1), Event(1), Event(1)]
    row_thr = [Event(1), Event(1), Event(1), Event(1), Event(1), Event(0,1), Event(0,1), Event(1), Event(1)]

    timetable = Timetable([row_one, row_two, row_thr])
    print(timetable)

    print("-" * 80)

    # short example, shows ending times are not synchronized
    print(Timetable([[Event(2, 1)], [Event(1, 1), Event(1)], [Event(1, 2), Event(1)]]))

    print("-" * 80)

    # larger random example
    def random_row(l):
        import random
        res = []
        t = 0
        while t < l:
            x = random.random()
            if x < 0.1: res.append(Event(0, random.randint(1, 3)))
            elif x < 0.8: res.append(Event(min(random.randint(1, 5), l - t), random.randint(0, 1) * random.randint(0, 2)))
            else: res.append(Gap(min(random.randint(1, 5), l - t)))
            t += res[-1].duration
        return res

    print(Timetable([random_row(50) for _ in range(15)]))
爱*していゐ 2024-12-27 08:08:15

您需要一个函数将您的时间映射到 x 坐标。困难的是,您有时需要更多空间。这可以通过对象列表来解决,说明何时以及需要多少额外空间。

function timeToLocation(t)
  location = t * scale
  for (o : extraSpaceList)
    if o.when < t
      location = location + o.space
  return location

每当您尝试放置对象时,发现没有足够的空间(因为元素重叠),只需在所需的时刻插入更多空间即可,extraSpaceList.add({when=2s,space=4pixels})。逐一处理所有行,然后再次处理所有行以获得最终位置。

如果您将对象转换为具有开始时间和图形大小,这会更容易。那么 Gap 和 Event 之间就没有区别了。

You need a function to map your time to the x coordinate. The hard part is, you sometimes need more space at a moment. This can be solved with a list of objects, saying when and how much extra space is needed.

function timeToLocation(t)
  location = t * scale
  for (o : extraSpaceList)
    if o.when < t
      location = location + o.space
  return location

Whenever you try to place an object, and notice there isn't enough room (because the elements overlap), simply insert some more space at the desired moment, extraSpaceList.add({when=2s,space=4pixels}). Process all the rows one by one, and then all of them again for the final location.

This is easier if you transform your objects to have a start time and graphical size. Then there's no difference between Gap and Event.

胡渣熟男 2024-12-27 08:08:15

如果我理解你的问题......

对于每一列,在对每一行进行初始处理时跟踪行的零持续时间框的最大宽度。当您最终绘制每个真实事件时,它会从其列的最大宽度值的末尾开始。 (我假设您已经在计算列的起始位置。)

在上面的彩色图表中,处理第 1 行将为您提供最大零持续时间宽度(例如)[0, 10, 0, 0, 0, 10, 0]。第 2 行不会改变它。第 3 行将其更改为 [0, 10, 0, 0, 0, 20, 0]。

当您绘制真实事件时,它将从其 column_start + 列 max-zero-duration-width 开始。

If I understand your question ...

For each column, keep track of the maximum width of the rows' zero-duration boxes as you're doing an initial processing of each row. When you finally draw each real event, it starts at the end of the max width value for its column. (I'm assuming you're already calculating where the start of the column is.)

In your colorful graph above, processing row 1 would give you maximum zero-duration widths of (for example) [0, 10, 0, 0, 0, 10, 0]. Row 2 would not change it. Row 3 would change it to [0, 10, 0, 0, 0, 20, 0].

When you draw a real event, it will start at its column_start + column max-zero-duration-width.

兰花执着 2024-12-27 08:08:15

我发现您的事件代码存在几个问题:1)无法知道要为位移分配多少空间,无论事件持续时间是否为零; 2)如果你有(比如在第四行)一个需要 3 个单位并从 5 开始的事件,它将完全绘制在 5 和 6 槽中,而不会达到 7。

怀疑你将需要类似的东西:

class Event(object):
    def __init__(self, duration, displacement=0):
        self.duration = duration
        self.displacement = displacement
        self.width = max(STANDARD_WIDTH, duration+displacement)
        #additional data

    def self.draw(self, start_coordinate):
        """draw duration * 10 pixels in black"""
        return self.width * 10

我 将图表作为表格,我不确定如何解决上面提出的第二个问题。

此外,还需要两次:第一次计算出小时块的各种列宽,第二次使用该信息绘制行。

I see a couple problems with your Event code as it stands: 1) there is no way to tell how much space to allocate for displacement, whether the event is zero duration or not; 2) if you have (say in row four) an event that takes 3 units and starts at 5, it will be entirely drawn in the 5 and 6 slots without ever getting to 7.

I suspect you will need to something like:

class Event(object):
    def __init__(self, duration, displacement=0):
        self.duration = duration
        self.displacement = displacement
        self.width = max(STANDARD_WIDTH, duration+displacement)
        #additional data

    def self.draw(self, start_coordinate):
        """draw duration * 10 pixels in black"""
        return self.width * 10

Without treating the graph as a table, I am not sure how to get around the second issue I raised above.

Also, it will take two passes: the first pass to figure out the various column widths of your hour blocks, the second to use that information to draw the rows.

枕花眠 2024-12-27 08:08:15

我建议使用事件本身的图标和持续时间的栏。

I recommend to use an icon for the event itself and a bar for duration.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文