Simplify timeline types: merge video/audio to media, remove trackCell/blockCells indirection
This commit is contained in:
@@ -17,10 +17,8 @@
|
|||||||
--cue-bg: rgba(58, 24, 0, 0.7);
|
--cue-bg: rgba(58, 24, 0, 0.7);
|
||||||
--light-color: #c8e;
|
--light-color: #c8e;
|
||||||
--light-bg: rgba(42, 10, 42, 0.55);
|
--light-bg: rgba(42, 10, 42, 0.55);
|
||||||
--video-color: #4d4;
|
--media-color: #4d4;
|
||||||
--video-bg: rgba(10, 42, 10, 0.55);
|
--media-bg: rgba(10, 42, 10, 0.55);
|
||||||
--audio-color: #58f;
|
|
||||||
--audio-bg: rgba(10, 10, 42, 0.55);
|
|
||||||
--delay-color: #999;
|
--delay-color: #999;
|
||||||
--delay-bg: rgba(26, 26, 26, 0.55);
|
--delay-bg: rgba(26, 26, 26, 0.55);
|
||||||
--infinity-color: #666;
|
--infinity-color: #666;
|
||||||
@@ -106,8 +104,7 @@ header h1 { font-size: 16px; font-weight: 600; letter-spacing: 0.05em; }
|
|||||||
border-bottom: 1px solid rgba(255, 204, 0, 0.3);
|
border-bottom: 1px solid rgba(255, 204, 0, 0.3);
|
||||||
}
|
}
|
||||||
.block.light { color: var(--light-color); border-color: var(--light-color); background: var(--light-bg); }
|
.block.light { color: var(--light-color); border-color: var(--light-color); background: var(--light-bg); }
|
||||||
.block.video { color: var(--video-color); border-color: var(--video-color); background: var(--video-bg); }
|
.block.media { color: var(--media-color); border-color: var(--media-color); background: var(--media-bg); }
|
||||||
.block.audio { color: var(--audio-color); border-color: var(--audio-color); background: var(--audio-bg); }
|
|
||||||
.block.delay { color: var(--delay-color); border-color: var(--delay-color); background: var(--delay-bg); }
|
.block.delay { color: var(--delay-color); border-color: var(--delay-color); background: var(--delay-bg); }
|
||||||
|
|
||||||
.hook {
|
.hook {
|
||||||
|
|||||||
@@ -11,27 +11,27 @@
|
|||||||
{"id": "block_q10_preshow", "type": "cue", "name": "Q10 Preshow"},
|
{"id": "block_q10_preshow", "type": "cue", "name": "Q10 Preshow"},
|
||||||
{"id": "block_preshow_wash", "type": "light", "track": "track_lighting_a", "name": "Preshow Wash"},
|
{"id": "block_preshow_wash", "type": "light", "track": "track_lighting_a", "name": "Preshow Wash"},
|
||||||
{"id": "block_warm_70", "type": "light", "track": "track_lighting_b", "name": "Warm 70%"},
|
{"id": "block_warm_70", "type": "light", "track": "track_lighting_b", "name": "Warm 70%"},
|
||||||
{"id": "block_preshow_loop", "type": "video", "track": "track_video", "name": "Preshow Loop", "loop": true},
|
{"id": "block_preshow_loop", "type": "media", "track": "track_video", "name": "Preshow Loop", "loop": true},
|
||||||
{"id": "block_preshow_music", "type": "audio", "track": "track_audio", "name": "Preshow Music", "loop": true},
|
{"id": "block_preshow_music", "type": "media", "track": "track_audio", "name": "Preshow Music", "loop": true},
|
||||||
{"id": "block_q11_house_open", "type": "cue", "name": "Q11 House Open"},
|
{"id": "block_q11_house_open", "type": "cue", "name": "Q11 House Open"},
|
||||||
{"id": "block_q12_top_of_show", "type": "cue", "name": "Q12 Top of Show"},
|
{"id": "block_q12_top_of_show", "type": "cue", "name": "Q12 Top of Show"},
|
||||||
{"id": "block_cool_50", "type": "light", "track": "track_lighting_b", "name": "Cool 50%"},
|
{"id": "block_cool_50", "type": "light", "track": "track_lighting_b", "name": "Cool 50%"},
|
||||||
{"id": "block_3s_delay", "type": "delay", "track": "track_video", "name": "3s Delay"},
|
{"id": "block_3s_delay", "type": "delay", "track": "track_video", "name": "3s Delay"},
|
||||||
{"id": "block_sc1_focus", "type": "light", "track": "track_lighting_a", "name": "SC1 Focus"},
|
{"id": "block_sc1_focus", "type": "light", "track": "track_lighting_a", "name": "SC1 Focus"},
|
||||||
{"id": "block_sc1_blue_80", "type": "light", "track": "track_lighting_b", "name": "SC1 Blue 80%"},
|
{"id": "block_sc1_blue_80", "type": "light", "track": "track_lighting_b", "name": "SC1 Blue 80%"},
|
||||||
{"id": "block_sc1_projection", "type": "video", "track": "track_video", "name": "Sc1 Projection"},
|
{"id": "block_sc1_projection", "type": "media", "track": "track_video", "name": "Sc1 Projection"},
|
||||||
{"id": "block_lightning_flash", "type": "video", "track": "track_video_ovl", "name": "Lightning Flash"},
|
{"id": "block_lightning_flash", "type": "media", "track": "track_video_ovl", "name": "Lightning Flash"},
|
||||||
{"id": "block_storm_ambience", "type": "audio", "track": "track_audio", "name": "Storm Ambience", "loop": true},
|
{"id": "block_storm_ambience", "type": "media", "track": "track_audio", "name": "Storm Ambience", "loop": true},
|
||||||
{"id": "block_q13_sc1_dialog", "type": "cue", "name": "Q13 Sc1 Dialog"},
|
{"id": "block_q13_sc1_dialog", "type": "cue", "name": "Q13 Sc1 Dialog"},
|
||||||
{"id": "block_wave_overlay", "type": "video", "track": "track_video_ovl", "name": "Wave Overlay"},
|
{"id": "block_wave_overlay", "type": "media", "track": "track_video_ovl", "name": "Wave Overlay"},
|
||||||
{"id": "block_dialog_spots", "type": "light", "track": "track_lighting_a", "name": "Dialog Spots"},
|
{"id": "block_dialog_spots", "type": "light", "track": "track_lighting_a", "name": "Dialog Spots"},
|
||||||
{"id": "block_warm_90", "type": "light", "track": "track_lighting_b", "name": "Warm 90%"},
|
{"id": "block_warm_90", "type": "light", "track": "track_lighting_b", "name": "Warm 90%"},
|
||||||
{"id": "block_dialog_underscore", "type": "audio", "track": "track_audio", "name": "Dialog Underscore"},
|
{"id": "block_dialog_underscore", "type": "media", "track": "track_audio", "name": "Dialog Underscore"},
|
||||||
{"id": "block_q14_sc2_trans", "type": "cue", "name": "Q14 Sc2 Trans"},
|
{"id": "block_q14_sc2_trans", "type": "cue", "name": "Q14 Sc2 Trans"},
|
||||||
{"id": "block_sc2_focus", "type": "light", "track": "track_lighting_a", "name": "SC2 Focus"},
|
{"id": "block_sc2_focus", "type": "light", "track": "track_lighting_a", "name": "SC2 Focus"},
|
||||||
{"id": "block_sc2_amber_60", "type": "light", "track": "track_lighting_b", "name": "SC2 Amber 60%"},
|
{"id": "block_sc2_amber_60", "type": "light", "track": "track_lighting_b", "name": "SC2 Amber 60%"},
|
||||||
{"id": "block_sc2_background", "type": "video", "track": "track_video", "name": "Sc2 Background", "loop": true},
|
{"id": "block_sc2_background", "type": "media", "track": "track_video", "name": "Sc2 Background", "loop": true},
|
||||||
{"id": "block_sc2_atmos", "type": "audio", "track": "track_audio", "name": "SC2 Atmos", "loop": true}
|
{"id": "block_sc2_atmos", "type": "media", "track": "track_audio", "name": "SC2 Atmos", "loop": true}
|
||||||
],
|
],
|
||||||
"triggers": [
|
"triggers": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ type TimelineCell struct {
|
|||||||
Event string `json:"event,omitempty"`
|
Event string `json:"event,omitempty"`
|
||||||
IsTitle bool `json:"is_title,omitempty"`
|
IsTitle bool `json:"is_title,omitempty"`
|
||||||
IsSignal bool `json:"is_signal,omitempty"`
|
IsSignal bool `json:"is_signal,omitempty"`
|
||||||
|
IsGap bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type cellID struct {
|
type cellID struct {
|
||||||
@@ -72,24 +73,21 @@ type exclusiveGroup struct {
|
|||||||
members []cellID
|
members []cellID
|
||||||
}
|
}
|
||||||
|
|
||||||
type trackCell struct {
|
|
||||||
cell TimelineCell
|
|
||||||
isGap bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type timelineBuilder struct {
|
type timelineBuilder struct {
|
||||||
|
show Show
|
||||||
blocks map[string]Block
|
blocks map[string]Block
|
||||||
tracks []Track
|
tracks []Track
|
||||||
trackIdx map[string]int
|
trackIdx map[string]int
|
||||||
startSigs map[string][]TriggerTarget
|
startSigs map[string][]TriggerTarget
|
||||||
|
|
||||||
trackCells [][]trackCell
|
trackCells [][]TimelineCell
|
||||||
constraints []constraint
|
constraints []constraint
|
||||||
exclusives []exclusiveGroup
|
exclusives []exclusiveGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildTimeline(show Show) (Timeline, error) {
|
func newTimelineBuilder(show Show) *timelineBuilder {
|
||||||
b := &timelineBuilder{
|
b := &timelineBuilder{
|
||||||
|
show: show,
|
||||||
blocks: map[string]Block{},
|
blocks: map[string]Block{},
|
||||||
trackIdx: map[string]int{},
|
trackIdx: map[string]int{},
|
||||||
startSigs: map[string][]TriggerTarget{},
|
startSigs: map[string][]TriggerTarget{},
|
||||||
@@ -110,10 +108,16 @@ func BuildTimeline(show Show) (Timeline, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.trackCells = make([][]trackCell, len(b.tracks))
|
b.trackCells = make([][]TimelineCell, len(b.tracks))
|
||||||
|
|
||||||
b.buildCells(show)
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildTimeline(show Show) (Timeline, error) {
|
||||||
|
b := newTimelineBuilder(show)
|
||||||
|
|
||||||
|
b.buildCells()
|
||||||
|
b.buildConstraints()
|
||||||
b.assignRows()
|
b.assignRows()
|
||||||
|
|
||||||
return Timeline{
|
return Timeline{
|
||||||
@@ -123,16 +127,6 @@ func BuildTimeline(show Show) (Timeline, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) appendCell(trackID string, cell TimelineCell) cellID {
|
|
||||||
idx, ok := b.trackIdx[trackID]
|
|
||||||
if !ok {
|
|
||||||
return cellID{-1, -1}
|
|
||||||
}
|
|
||||||
i := len(b.trackCells[idx])
|
|
||||||
b.trackCells[idx] = append(b.trackCells[idx], trackCell{cell: cell})
|
|
||||||
return cellID{track: idx, index: i}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *timelineBuilder) addConstraint(kind string, a, b2 cellID) {
|
func (b *timelineBuilder) addConstraint(kind string, a, b2 cellID) {
|
||||||
if a.track < 0 || b2.track < 0 {
|
if a.track < 0 || b2.track < 0 {
|
||||||
return
|
return
|
||||||
@@ -151,53 +145,64 @@ func (b *timelineBuilder) getTrack(blockID string) string {
|
|||||||
return block.Track
|
return block.Track
|
||||||
}
|
}
|
||||||
|
|
||||||
type blockCells struct {
|
func getCueCells(block Block) []TimelineCell {
|
||||||
start cellID
|
return []TimelineCell{{
|
||||||
title cellID
|
BlockID: block.ID,
|
||||||
fadeOut cellID
|
IsStart: true,
|
||||||
end cellID
|
IsEnd: true,
|
||||||
|
Event: "GO",
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) buildCells(show Show) {
|
func getBlockCells(block Block) []TimelineCell {
|
||||||
cells := map[string]blockCells{}
|
return []TimelineCell{
|
||||||
|
{BlockID: block.ID, IsStart: true, Event: "START"},
|
||||||
|
{BlockID: block.ID, IsTitle: true},
|
||||||
|
{BlockID: block.ID, Event: "FADE_OUT"},
|
||||||
|
{BlockID: block.ID, IsEnd: true, Event: "END"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, block := range show.Blocks {
|
func (b *timelineBuilder) findCell(blockID, event string) cellID {
|
||||||
|
trackID := b.getTrack(blockID)
|
||||||
|
if trackID == "" {
|
||||||
|
return cellID{-1, -1}
|
||||||
|
}
|
||||||
|
track := b.trackIdx[trackID]
|
||||||
|
for i, c := range b.trackCells[track] {
|
||||||
|
if !c.IsGap && c.BlockID == blockID && c.Event == event {
|
||||||
|
return cellID{track: track, index: i}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cellID{-1, -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *timelineBuilder) buildCells() {
|
||||||
|
for _, block := range b.show.Blocks {
|
||||||
trackID := b.getTrack(block.ID)
|
trackID := b.getTrack(block.ID)
|
||||||
if trackID == "" {
|
if trackID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if block.Type == "cue" {
|
idx := b.trackIdx[trackID]
|
||||||
cueID := b.appendCell(trackID, TimelineCell{
|
var cells []TimelineCell
|
||||||
BlockID: block.ID,
|
switch block.Type {
|
||||||
IsStart: true,
|
case "cue":
|
||||||
IsEnd: true,
|
cells = getCueCells(block)
|
||||||
Event: "GO",
|
default:
|
||||||
})
|
cells = getBlockCells(block)
|
||||||
cells[block.ID] = blockCells{start: cueID, title: cueID, fadeOut: cueID, end: cueID}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
startID := b.appendCell(trackID, TimelineCell{BlockID: block.ID, IsStart: true, Event: "START"})
|
b.trackCells[idx] = append(b.trackCells[idx], cells...)
|
||||||
titleID := b.appendCell(trackID, TimelineCell{BlockID: block.ID, IsTitle: true})
|
|
||||||
fadeOutID := b.appendCell(trackID, TimelineCell{BlockID: block.ID, Event: "FADE_OUT"})
|
|
||||||
endID := b.appendCell(trackID, TimelineCell{BlockID: block.ID, IsEnd: true, Event: "END"})
|
|
||||||
cells[block.ID] = blockCells{start: startID, title: titleID, fadeOut: fadeOutID, end: endID}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, trigger := range show.Triggers {
|
func (b *timelineBuilder) buildConstraints() {
|
||||||
|
for _, trigger := range b.show.Triggers {
|
||||||
if trigger.Source.Signal == "START" {
|
if trigger.Source.Signal == "START" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCell := cells[trigger.Source.Block]
|
sourceID := b.findCell(trigger.Source.Block, trigger.Source.Signal)
|
||||||
var sourceID cellID
|
if sourceID.track < 0 {
|
||||||
switch trigger.Source.Signal {
|
|
||||||
case "GO":
|
|
||||||
sourceID = sourceCell.start
|
|
||||||
case "END":
|
|
||||||
sourceID = sourceCell.end
|
|
||||||
case "FADE_OUT":
|
|
||||||
sourceID = sourceCell.fadeOut
|
|
||||||
default:
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,16 +211,8 @@ func (b *timelineBuilder) buildCells(show Show) {
|
|||||||
|
|
||||||
allTargets := b.expandTargets(trigger.Targets)
|
allTargets := b.expandTargets(trigger.Targets)
|
||||||
for _, target := range allTargets {
|
for _, target := range allTargets {
|
||||||
tc := cells[target.Block]
|
targetID := b.findCell(target.Block, target.Hook)
|
||||||
var targetID cellID
|
if targetID.track < 0 {
|
||||||
switch target.Hook {
|
|
||||||
case "START":
|
|
||||||
targetID = tc.start
|
|
||||||
case "END":
|
|
||||||
targetID = tc.end
|
|
||||||
case "FADE_OUT":
|
|
||||||
targetID = tc.fadeOut
|
|
||||||
default:
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if sourceID.track == targetID.track {
|
if sourceID.track == targetID.track {
|
||||||
@@ -232,7 +229,6 @@ func (b *timelineBuilder) buildCells(show Show) {
|
|||||||
}
|
}
|
||||||
b.exclusives = append(b.exclusives, group)
|
b.exclusives = append(b.exclusives, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) expandTargets(targets []TriggerTarget) []TriggerTarget {
|
func (b *timelineBuilder) expandTargets(targets []TriggerTarget) []TriggerTarget {
|
||||||
@@ -261,7 +257,7 @@ func (b *timelineBuilder) setSignal(id cellID) {
|
|||||||
if id.track < 0 {
|
if id.track < 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.trackCells[id.track][id.index].cell.IsSignal = true
|
b.trackCells[id.track][id.index].IsSignal = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *timelineBuilder) assignRows() {
|
func (b *timelineBuilder) assignRows() {
|
||||||
@@ -323,8 +319,8 @@ func (b *timelineBuilder) enforceExclusives() bool {
|
|||||||
if row >= len(b.trackCells[trackIdx]) {
|
if row >= len(b.trackCells[trackIdx]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tc := b.trackCells[trackIdx][row]
|
c := b.trackCells[trackIdx][row]
|
||||||
if tc.isGap || tc.cell.BlockID == "" {
|
if c.IsGap || c.BlockID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.insertGap(trackIdx, row)
|
b.insertGap(trackIdx, row)
|
||||||
@@ -340,9 +336,9 @@ func (b *timelineBuilder) rowOf(id cellID) int {
|
|||||||
|
|
||||||
func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
func (b *timelineBuilder) insertGap(track, beforeIndex int) {
|
||||||
cells := b.trackCells[track]
|
cells := b.trackCells[track]
|
||||||
newCells := make([]trackCell, 0, len(cells)+1)
|
newCells := make([]TimelineCell, 0, len(cells)+1)
|
||||||
newCells = append(newCells, cells[:beforeIndex]...)
|
newCells = append(newCells, cells[:beforeIndex]...)
|
||||||
newCells = append(newCells, trackCell{isGap: true})
|
newCells = append(newCells, TimelineCell{IsGap: true})
|
||||||
newCells = append(newCells, cells[beforeIndex:]...)
|
newCells = append(newCells, cells[beforeIndex:]...)
|
||||||
b.trackCells[track] = newCells
|
b.trackCells[track] = newCells
|
||||||
|
|
||||||
@@ -382,16 +378,16 @@ func (b *timelineBuilder) renderRows() []TimelineRow {
|
|||||||
activeBlock := ""
|
activeBlock := ""
|
||||||
for r := 0; r < maxLen; r++ {
|
for r := 0; r < maxLen; r++ {
|
||||||
if r < len(cells) {
|
if r < len(cells) {
|
||||||
tc := cells[r]
|
c := cells[r]
|
||||||
if tc.isGap {
|
if c.IsGap {
|
||||||
if activeBlock != "" {
|
if activeBlock != "" {
|
||||||
rows[r].Cells[trackIdx] = TimelineCell{BlockID: activeBlock}
|
rows[r].Cells[trackIdx] = TimelineCell{BlockID: activeBlock}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rows[r].Cells[trackIdx] = tc.cell
|
rows[r].Cells[trackIdx] = c
|
||||||
if tc.cell.BlockID != "" && !tc.cell.IsEnd {
|
if c.BlockID != "" && !c.IsEnd {
|
||||||
activeBlock = tc.cell.BlockID
|
activeBlock = c.BlockID
|
||||||
} else if tc.cell.IsEnd {
|
} else if c.IsEnd {
|
||||||
activeBlock = ""
|
activeBlock = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user