Ответ 1
Я не совсем уверен, но я думаю, что вы хотите:
- смещения в разных строках, которые имеют одинаковое время , синхронизируются, то есть имеют одинаковую горизонтальную позицию (первое смещение по каждой строке синхронизируется с первыми смещениями в других строках)
- "реальные" события и промежутки, которые имеют одинаковое время запуска, синхронизируются и поступают после перемещений с одинаковым временем запуска
- ширина события зависит от его продолжительности (и, возможно, от его смещения), но не при смещениях в других строках, т.е. время окончания не синхронизируется
Если вы хотите, чтобы время окончания было синхронизировано, вам нужно будет объяснить, как; но я не вижу очевидного пути.
Затем вы получите следующее решение для своей исходной проблемы (capitals = события, строчные буквы = смещения, точки = пробелы, пробел = "ожидание синхронизации", цифры - время начала событий):
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)]))