Merge Timeline and timelineBuilder into single struct, remove cells()/setCells()
This commit is contained in:
@@ -48,6 +48,11 @@ type TimelineTrack struct {
|
|||||||
type Timeline struct {
|
type Timeline struct {
|
||||||
Tracks []TimelineTrack `json:"tracks"`
|
Tracks []TimelineTrack `json:"tracks"`
|
||||||
Blocks map[string]Block `json:"blocks"`
|
Blocks map[string]Block `json:"blocks"`
|
||||||
|
|
||||||
|
show Show `json:"-"`
|
||||||
|
trackIdx map[string]int `json:"-"`
|
||||||
|
constraints []constraint `json:"-"`
|
||||||
|
exclusives []exclusiveGroup `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimelineCell struct {
|
type TimelineCell struct {
|
||||||
@@ -76,47 +81,7 @@ type exclusiveGroup struct {
|
|||||||
members []cellID
|
members []cellID
|
||||||
}
|
}
|
||||||
|
|
||||||
type timelineBuilder struct {
|
|
||||||
show Show
|
|
||||||
blocks map[string]Block
|
|
||||||
tracks []Track
|
|
||||||
trackIdx map[string]int
|
|
||||||
trackCells [][]TimelineCell
|
|
||||||
constraints []constraint
|
|
||||||
exclusives []exclusiveGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTimelineBuilder(show Show) *timelineBuilder {
|
|
||||||
b := &timelineBuilder{
|
|
||||||
show: show,
|
|
||||||
blocks: map[string]Block{},
|
|
||||||
trackIdx: map[string]int{},
|
|
||||||
}
|
|
||||||
|
|
||||||
b.tracks = append(b.tracks, Track{ID: cueTrackID, Name: "Cue"})
|
|
||||||
b.trackIdx[cueTrackID] = 0
|
|
||||||
for i, track := range show.Tracks {
|
|
||||||
b.tracks = append(b.tracks, track)
|
|
||||||
b.trackIdx[track.ID] = i + 1
|
|
||||||
}
|
|
||||||
for _, block := range show.Blocks {
|
|
||||||
if block.Type == "cue" {
|
|
||||||
block.Track = cueTrackID
|
|
||||||
}
|
|
||||||
b.blocks[block.ID] = block
|
|
||||||
}
|
|
||||||
|
|
||||||
b.trackCells = make([][]TimelineCell, len(b.tracks))
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateShow(show Show) error {
|
func validateShow(show Show) error {
|
||||||
blockSet := map[string]bool{}
|
|
||||||
for _, block := range show.Blocks {
|
|
||||||
blockSet[block.ID] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
startTargeted := map[string]bool{}
|
startTargeted := map[string]bool{}
|
||||||
for _, trigger := range show.Triggers {
|
for _, trigger := range show.Triggers {
|
||||||
for _, target := range trigger.Targets {
|
for _, target := range trigger.Targets {
|
||||||
@@ -143,25 +108,34 @@ func BuildTimeline(show Show) (Timeline, error) {
|
|||||||
return Timeline{}, err
|
return Timeline{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b := newTimelineBuilder(show)
|
tl := Timeline{
|
||||||
|
show: show,
|
||||||
b.buildCells()
|
Blocks: map[string]Block{},
|
||||||
b.buildConstraints()
|
trackIdx: map[string]int{},
|
||||||
b.assignRows()
|
|
||||||
|
|
||||||
tracks := make([]TimelineTrack, len(b.tracks))
|
|
||||||
for i, t := range b.tracks {
|
|
||||||
tracks[i] = TimelineTrack{Track: t, Cells: b.trackCells[i]}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Timeline{
|
tl.Tracks = append(tl.Tracks, TimelineTrack{Track: Track{ID: cueTrackID, Name: "Cue"}})
|
||||||
Tracks: tracks,
|
tl.trackIdx[cueTrackID] = 0
|
||||||
Blocks: b.blocks,
|
for i, track := range show.Tracks {
|
||||||
}, nil
|
tl.Tracks = append(tl.Tracks, TimelineTrack{Track: track})
|
||||||
|
tl.trackIdx[track.ID] = i + 1
|
||||||
|
}
|
||||||
|
for _, block := range show.Blocks {
|
||||||
|
if block.Type == "cue" {
|
||||||
|
block.Track = cueTrackID
|
||||||
|
}
|
||||||
|
tl.Blocks[block.ID] = block
|
||||||
|
}
|
||||||
|
|
||||||
|
tl.buildCells()
|
||||||
|
tl.buildConstraints()
|
||||||
|
tl.assignRows()
|
||||||
|
|
||||||
|
return tl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) addConstraint(kind string, a, b2 cellID) {
|
func (tl *Timeline) addConstraint(kind string, a, b cellID) {
|
||||||
b.constraints = append(b.constraints, constraint{kind: kind, a: a, b: b2})
|
tl.constraints = append(tl.constraints, constraint{kind: kind, a: a, b: b})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCueCells(block Block) []TimelineCell {
|
func getCueCells(block Block) []TimelineCell {
|
||||||
@@ -182,9 +156,9 @@ func getBlockCells(block Block) []TimelineCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) findCell(blockID, event string) cellID {
|
func (tl *Timeline) findCell(blockID, event string) cellID {
|
||||||
track := b.trackIdx[b.blocks[blockID].Track]
|
track := tl.trackIdx[tl.Blocks[blockID].Track]
|
||||||
for i, c := range b.trackCells[track] {
|
for i, c := range tl.Tracks[track].Cells {
|
||||||
if !c.IsGap && c.BlockID == blockID && c.Event == event {
|
if !c.IsGap && c.BlockID == blockID && c.Event == event {
|
||||||
return cellID{track: track, index: i}
|
return cellID{track: track, index: i}
|
||||||
}
|
}
|
||||||
@@ -192,14 +166,14 @@ func (b *timelineBuilder) findCell(blockID, event string) cellID {
|
|||||||
panic("cell not found: " + blockID + " " + event)
|
panic("cell not found: " + blockID + " " + event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) endChainsSameTrack(blockID string) bool {
|
func (tl *Timeline) endChainsSameTrack(blockID string) bool {
|
||||||
trackID := b.blocks[blockID].Track
|
trackID := tl.Blocks[blockID].Track
|
||||||
for _, trigger := range b.show.Triggers {
|
for _, trigger := range tl.show.Triggers {
|
||||||
if trigger.Source.Block != blockID || trigger.Source.Signal != "END" {
|
if trigger.Source.Block != blockID || trigger.Source.Signal != "END" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, target := range trigger.Targets {
|
for _, target := range trigger.Targets {
|
||||||
if target.Hook == "START" && b.blocks[target.Block].Track == trackID {
|
if target.Hook == "START" && tl.Blocks[target.Block].Track == trackID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,10 +181,10 @@ func (b *timelineBuilder) endChainsSameTrack(blockID string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) buildCells() {
|
func (tl *Timeline) buildCells() {
|
||||||
for _, sb := range b.show.Blocks {
|
for _, sb := range tl.show.Blocks {
|
||||||
block := b.blocks[sb.ID]
|
block := tl.Blocks[sb.ID]
|
||||||
idx := b.trackIdx[block.Track]
|
idx := tl.trackIdx[block.Track]
|
||||||
var cells []TimelineCell
|
var cells []TimelineCell
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case "cue":
|
case "cue":
|
||||||
@@ -218,58 +192,57 @@ func (b *timelineBuilder) buildCells() {
|
|||||||
default:
|
default:
|
||||||
cells = getBlockCells(block)
|
cells = getBlockCells(block)
|
||||||
}
|
}
|
||||||
b.trackCells[idx] = append(b.trackCells[idx], cells...)
|
tl.Tracks[idx].Cells = append(tl.Tracks[idx].Cells, cells...)
|
||||||
if block.Type != "cue" && !b.endChainsSameTrack(block.ID) {
|
if block.Type != "cue" && !tl.endChainsSameTrack(block.ID) {
|
||||||
b.trackCells[idx] = append(b.trackCells[idx], TimelineCell{IsGap: true, IsBreak: true})
|
tl.Tracks[idx].Cells = append(tl.Tracks[idx].Cells, TimelineCell{IsGap: true, IsBreak: true})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) buildConstraints() {
|
func (tl *Timeline) buildConstraints() {
|
||||||
for _, trigger := range b.show.Triggers {
|
for _, trigger := range tl.show.Triggers {
|
||||||
sourceID := b.findCell(trigger.Source.Block, trigger.Source.Signal)
|
sourceID := tl.findCell(trigger.Source.Block, trigger.Source.Signal)
|
||||||
|
|
||||||
group := exclusiveGroup{members: []cellID{sourceID}}
|
group := exclusiveGroup{members: []cellID{sourceID}}
|
||||||
hasCrossTrack := false
|
hasCrossTrack := false
|
||||||
|
|
||||||
for _, target := range trigger.Targets {
|
for _, target := range trigger.Targets {
|
||||||
targetID := b.findCell(target.Block, target.Hook)
|
targetID := tl.findCell(target.Block, target.Hook)
|
||||||
if sourceID.track == targetID.track {
|
if sourceID.track == targetID.track {
|
||||||
b.addConstraint("next_row", sourceID, targetID)
|
tl.addConstraint("next_row", sourceID, targetID)
|
||||||
} else {
|
} else {
|
||||||
b.addConstraint("same_row", sourceID, targetID)
|
tl.addConstraint("same_row", sourceID, targetID)
|
||||||
hasCrossTrack = true
|
hasCrossTrack = true
|
||||||
}
|
}
|
||||||
group.members = append(group.members, targetID)
|
group.members = append(group.members, targetID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasCrossTrack {
|
if hasCrossTrack {
|
||||||
b.trackCells[sourceID.track][sourceID.index].IsSignal = true
|
tl.Tracks[sourceID.track].Cells[sourceID.index].IsSignal = true
|
||||||
}
|
}
|
||||||
b.exclusives = append(b.exclusives, group)
|
tl.exclusives = append(tl.exclusives, group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tl *Timeline) assignRows() {
|
||||||
func (b *timelineBuilder) assignRows() {
|
|
||||||
for {
|
for {
|
||||||
found := false
|
found := false
|
||||||
for _, c := range b.constraints {
|
for _, c := range tl.constraints {
|
||||||
aRow := b.rowOf(c.a)
|
aRow := tl.rowOf(c.a)
|
||||||
bRow := b.rowOf(c.b)
|
bRow := tl.rowOf(c.b)
|
||||||
|
|
||||||
switch c.kind {
|
switch c.kind {
|
||||||
case "same_row":
|
case "same_row":
|
||||||
if aRow < bRow {
|
if aRow < bRow {
|
||||||
b.insertGap(c.a.track, c.a.index)
|
tl.insertGap(c.a.track, c.a.index)
|
||||||
found = true
|
found = true
|
||||||
} else if bRow < aRow {
|
} else if bRow < aRow {
|
||||||
b.insertGap(c.b.track, c.b.index)
|
tl.insertGap(c.b.track, c.b.index)
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
case "next_row":
|
case "next_row":
|
||||||
if bRow <= aRow {
|
if bRow <= aRow {
|
||||||
b.insertGap(c.b.track, c.b.index)
|
tl.insertGap(c.b.track, c.b.index)
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,7 +251,7 @@ func (b *timelineBuilder) assignRows() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
found = b.enforceExclusives()
|
found = tl.enforceExclusives()
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
break
|
break
|
||||||
@@ -286,50 +259,51 @@ func (b *timelineBuilder) assignRows() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) enforceExclusives() bool {
|
func (tl *Timeline) enforceExclusives() bool {
|
||||||
for _, g := range b.exclusives {
|
for _, g := range tl.exclusives {
|
||||||
if len(g.members) == 0 {
|
if len(g.members) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
row := b.rowOf(g.members[0])
|
row := tl.rowOf(g.members[0])
|
||||||
allAligned := true
|
allAligned := true
|
||||||
memberTracks := map[int]bool{}
|
memberTracks := map[int]bool{}
|
||||||
for _, m := range g.members {
|
for _, m := range g.members {
|
||||||
memberTracks[m.track] = true
|
memberTracks[m.track] = true
|
||||||
if b.rowOf(m) != row {
|
if tl.rowOf(m) != row {
|
||||||
allAligned = false
|
allAligned = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !allAligned {
|
if !allAligned {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for trackIdx := range b.trackCells {
|
for trackIdx := range tl.Tracks {
|
||||||
if memberTracks[trackIdx] {
|
if memberTracks[trackIdx] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if row >= len(b.trackCells[trackIdx]) {
|
if row >= len(tl.Tracks[trackIdx].Cells) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c := b.trackCells[trackIdx][row]
|
c := tl.Tracks[trackIdx].Cells[row]
|
||||||
if c.IsGap || c.BlockID == "" {
|
if c.IsGap || c.BlockID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.insertGap(trackIdx, row)
|
tl.insertGap(trackIdx, row)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) rowOf(id cellID) int {
|
func (tl *Timeline) rowOf(id cellID) int {
|
||||||
return id.index
|
return id.index
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) isAllGapRow(row, exceptTrack int) bool {
|
func (tl *Timeline) isAllGapRow(row, exceptTrack int) bool {
|
||||||
for trackIdx, cells := range b.trackCells {
|
for trackIdx := range tl.Tracks {
|
||||||
if trackIdx == exceptTrack {
|
if trackIdx == exceptTrack {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
cells := tl.Tracks[trackIdx].Cells
|
||||||
if row >= len(cells) {
|
if row >= len(cells) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -340,11 +314,12 @@ func (b *timelineBuilder) isAllGapRow(row, exceptTrack int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) removeGapAt(track, index int) {
|
func (tl *Timeline) removeGapAt(track, index int) {
|
||||||
b.trackCells[track] = append(b.trackCells[track][:index], b.trackCells[track][index+1:]...)
|
cells := tl.Tracks[track].Cells
|
||||||
|
tl.Tracks[track].Cells = append(cells[:index], cells[index+1:]...)
|
||||||
|
|
||||||
for i := range b.constraints {
|
for i := range tl.constraints {
|
||||||
c := &b.constraints[i]
|
c := &tl.constraints[i]
|
||||||
if c.a.track == track && c.a.index > index {
|
if c.a.track == track && c.a.index > index {
|
||||||
c.a.index--
|
c.a.index--
|
||||||
}
|
}
|
||||||
@@ -352,9 +327,9 @@ func (b *timelineBuilder) removeGapAt(track, index int) {
|
|||||||
c.b.index--
|
c.b.index--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range b.exclusives {
|
for i := range tl.exclusives {
|
||||||
for j := range b.exclusives[i].members {
|
for j := range tl.exclusives[i].members {
|
||||||
m := &b.exclusives[i].members[j]
|
m := &tl.exclusives[i].members[j]
|
||||||
if m.track == track && m.index > index {
|
if m.track == track && m.index > index {
|
||||||
m.index--
|
m.index--
|
||||||
}
|
}
|
||||||
@@ -362,10 +337,10 @@ func (b *timelineBuilder) removeGapAt(track, index int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
func (tl *Timeline) insertGap(track, beforeIndex int) {
|
||||||
for {
|
for {
|
||||||
blocked := false
|
blocked := false
|
||||||
for _, c := range b.constraints {
|
for _, c := range tl.constraints {
|
||||||
if c.kind == "next_row" && c.a.track == track && c.b.track == track && c.a.index == beforeIndex-1 && c.b.index == beforeIndex {
|
if c.kind == "next_row" && c.a.track == track && c.b.track == track && c.a.index == beforeIndex-1 && c.b.index == beforeIndex {
|
||||||
beforeIndex = c.a.index
|
beforeIndex = c.a.index
|
||||||
blocked = true
|
blocked = true
|
||||||
@@ -377,16 +352,16 @@ func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.isAllGapRow(beforeIndex, track) {
|
if tl.isAllGapRow(beforeIndex, track) {
|
||||||
for trackIdx := range b.trackCells {
|
for trackIdx := range tl.Tracks {
|
||||||
if trackIdx == track {
|
if trackIdx == track {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if beforeIndex >= len(b.trackCells[trackIdx]) {
|
if beforeIndex >= len(tl.Tracks[trackIdx].Cells) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if b.trackCells[trackIdx][beforeIndex].IsGap {
|
if tl.Tracks[trackIdx].Cells[beforeIndex].IsGap {
|
||||||
b.removeGapAt(trackIdx, beforeIndex)
|
tl.removeGapAt(trackIdx, beforeIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -394,7 +369,7 @@ func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
|||||||
|
|
||||||
gap := TimelineCell{IsGap: true}
|
gap := TimelineCell{IsGap: true}
|
||||||
for i := beforeIndex - 1; i >= 0; i-- {
|
for i := beforeIndex - 1; i >= 0; i-- {
|
||||||
c := b.trackCells[track][i]
|
c := tl.Tracks[track].Cells[i]
|
||||||
if c.IsGap {
|
if c.IsGap {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -404,15 +379,15 @@ func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
cells := b.trackCells[track]
|
cells := tl.Tracks[track].Cells
|
||||||
newCells := make([]TimelineCell, 0, len(cells)+1)
|
newCells := make([]TimelineCell, 0, len(cells)+1)
|
||||||
newCells = append(newCells, cells[:beforeIndex]...)
|
newCells = append(newCells, cells[:beforeIndex]...)
|
||||||
newCells = append(newCells, gap)
|
newCells = append(newCells, gap)
|
||||||
newCells = append(newCells, cells[beforeIndex:]...)
|
newCells = append(newCells, cells[beforeIndex:]...)
|
||||||
b.trackCells[track] = newCells
|
tl.Tracks[track].Cells = newCells
|
||||||
|
|
||||||
for i := range b.constraints {
|
for i := range tl.constraints {
|
||||||
c := &b.constraints[i]
|
c := &tl.constraints[i]
|
||||||
if c.a.track == track && c.a.index >= beforeIndex {
|
if c.a.track == track && c.a.index >= beforeIndex {
|
||||||
c.a.index++
|
c.a.index++
|
||||||
}
|
}
|
||||||
@@ -420,14 +395,12 @@ func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
|||||||
c.b.index++
|
c.b.index++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range b.exclusives {
|
for i := range tl.exclusives {
|
||||||
for j := range b.exclusives[i].members {
|
for j := range tl.exclusives[i].members {
|
||||||
m := &b.exclusives[i].members[j]
|
m := &tl.exclusives[i].members[j]
|
||||||
if m.track == track && m.index >= beforeIndex {
|
if m.track == track && m.index >= beforeIndex {
|
||||||
m.index++
|
m.index++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user