Split mock show generation into methods and clean up randBlock
This commit is contained in:
@@ -25,181 +25,234 @@ var delayNamePool = []string{
|
|||||||
"1s Delay", "2s Delay", "3s Delay", "5s Delay", "Hold",
|
"1s Delay", "2s Delay", "3s Delay", "5s Delay", "Hold",
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateMockShow(numTracks, numScenes, avgCuesPerScene, avgBlocksPerCue int) *Show {
|
type chainable struct {
|
||||||
rng := rand.New(rand.NewPCG(42, 0))
|
|
||||||
|
|
||||||
show := &Show{}
|
|
||||||
blockIdx := 0
|
|
||||||
curCueName := ""
|
|
||||||
nextBlockID := func(trackIdx int) string {
|
|
||||||
id := fmt.Sprintf("%s-t%d-b%d", curCueName, trackIdx, blockIdx)
|
|
||||||
blockIdx++
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
names := make([]string, len(trackNamePool))
|
|
||||||
copy(names, trackNamePool)
|
|
||||||
rng.Shuffle(len(names), func(i, j int) {
|
|
||||||
names[i], names[j] = names[j], names[i]
|
|
||||||
})
|
|
||||||
for i := range numTracks {
|
|
||||||
name := names[i%len(names)]
|
|
||||||
if i >= len(names) {
|
|
||||||
name = fmt.Sprintf("%s %d", name, i/len(names)+1)
|
|
||||||
}
|
|
||||||
show.Tracks = append(show.Tracks, &Track{
|
|
||||||
ID: fmt.Sprintf("track_%d", i),
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
randBlock := func(trackIdx int) *Block {
|
|
||||||
r := rng.Float64()
|
|
||||||
var typ, name string
|
|
||||||
var loop bool
|
|
||||||
switch {
|
|
||||||
case r < 0.30:
|
|
||||||
typ, name = "light", lightNamePool[rng.IntN(len(lightNamePool))]
|
|
||||||
case r < 0.55:
|
|
||||||
typ, name = "media", mediaNamePool[rng.IntN(len(mediaNamePool))]
|
|
||||||
case r < 0.70:
|
|
||||||
typ, name, loop = "media", mediaNamePool[rng.IntN(len(mediaNamePool))], true
|
|
||||||
case r < 0.80:
|
|
||||||
typ, name = "delay", delayNamePool[rng.IntN(len(delayNamePool))]
|
|
||||||
default:
|
|
||||||
typ, name = "light", lightNamePool[rng.IntN(len(lightNamePool))]
|
|
||||||
}
|
|
||||||
return &Block{
|
|
||||||
ID: nextBlockID(trackIdx),
|
|
||||||
Type: typ,
|
|
||||||
Track: fmt.Sprintf("track_%d", trackIdx),
|
|
||||||
Name: name,
|
|
||||||
Loop: loop,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type chainable struct {
|
|
||||||
block *Block
|
block *Block
|
||||||
trackIdx int
|
trackIdx int
|
||||||
sameTrackOnly bool
|
sameTrackOnly bool
|
||||||
fromEnded bool
|
fromEnded bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockShowGen struct {
|
||||||
|
rng *rand.Rand
|
||||||
|
show *Show
|
||||||
|
numTracks int
|
||||||
|
blockIdx int
|
||||||
|
curCueName string
|
||||||
|
triggerIdx map[TriggerSource]*Trigger
|
||||||
|
needsEnd map[int]*Block
|
||||||
|
chainFrom []chainable
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateMockShow(numTracks, numScenes, avgCuesPerScene, avgBlocksPerCue int) *Show {
|
||||||
|
g := &mockShowGen{
|
||||||
|
rng: rand.New(rand.NewPCG(42, 0)),
|
||||||
|
show: &Show{},
|
||||||
|
numTracks: numTracks,
|
||||||
|
triggerIdx: map[TriggerSource]*Trigger{},
|
||||||
|
needsEnd: map[int]*Block{},
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerIdx := map[TriggerSource]*Trigger{}
|
g.generateTracks()
|
||||||
addTrigger := func(source TriggerSource, target TriggerTarget) {
|
|
||||||
if t := triggerIdx[source]; t != nil {
|
for scene := 1; scene <= numScenes; scene++ {
|
||||||
|
cuesInScene := 1 + g.rng.IntN(avgCuesPerScene*2)
|
||||||
|
for intra := 1; intra <= cuesInScene; intra++ {
|
||||||
|
g.generateCue(fmt.Sprintf("S%d Q%d", scene, intra), avgBlocksPerCue)
|
||||||
|
}
|
||||||
|
g.generateEndOfScene(scene)
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.show
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) generateTracks() {
|
||||||
|
names := make([]string, len(trackNamePool))
|
||||||
|
copy(names, trackNamePool)
|
||||||
|
g.rng.Shuffle(len(names), func(i, j int) {
|
||||||
|
names[i], names[j] = names[j], names[i]
|
||||||
|
})
|
||||||
|
for i := range g.numTracks {
|
||||||
|
name := names[i%len(names)]
|
||||||
|
if i >= len(names) {
|
||||||
|
name = fmt.Sprintf("%s %d", name, i/len(names)+1)
|
||||||
|
}
|
||||||
|
g.show.Tracks = append(g.show.Tracks, &Track{
|
||||||
|
ID: fmt.Sprintf("track_%d", i),
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) nextBlockID(trackIdx int) string {
|
||||||
|
id := fmt.Sprintf("%s-t%d-b%d", g.curCueName, trackIdx, g.blockIdx)
|
||||||
|
g.blockIdx++
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) randLight() Block {
|
||||||
|
return Block{
|
||||||
|
Type: "light",
|
||||||
|
Name: lightNamePool[g.rng.IntN(len(lightNamePool))],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) randMedia() Block {
|
||||||
|
return Block{
|
||||||
|
Type: "media",
|
||||||
|
Name: mediaNamePool[g.rng.IntN(len(mediaNamePool))],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) randLoopingMedia() Block {
|
||||||
|
return Block{
|
||||||
|
Type: "media",
|
||||||
|
Name: mediaNamePool[g.rng.IntN(len(mediaNamePool))],
|
||||||
|
Loop: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) randDelay() Block {
|
||||||
|
return Block{
|
||||||
|
Type: "delay",
|
||||||
|
Name: delayNamePool[g.rng.IntN(len(delayNamePool))],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) randBlock(trackIdx int) *Block {
|
||||||
|
r := g.rng.Float64()
|
||||||
|
var b Block
|
||||||
|
switch {
|
||||||
|
case r < 0.50:
|
||||||
|
b = g.randLight()
|
||||||
|
case r < 0.75:
|
||||||
|
b = g.randMedia()
|
||||||
|
case r < 0.90:
|
||||||
|
b = g.randLoopingMedia()
|
||||||
|
default:
|
||||||
|
b = g.randDelay()
|
||||||
|
}
|
||||||
|
b.ID = g.nextBlockID(trackIdx)
|
||||||
|
b.Track = fmt.Sprintf("track_%d", trackIdx)
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) addTrigger(source TriggerSource, target TriggerTarget) {
|
||||||
|
if t := g.triggerIdx[source]; t != nil {
|
||||||
t.Targets = append(t.Targets, target)
|
t.Targets = append(t.Targets, target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := &Trigger{Source: source, Targets: []TriggerTarget{target}}
|
t := &Trigger{Source: source, Targets: []TriggerTarget{target}}
|
||||||
show.Triggers = append(show.Triggers, t)
|
g.show.Triggers = append(g.show.Triggers, t)
|
||||||
triggerIdx[source] = t
|
g.triggerIdx[source] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
needsEnd := map[int]*Block{}
|
func (g *mockShowGen) endPreviousBlocks() []TriggerTarget {
|
||||||
var chainFrom []chainable
|
var cueTargets []TriggerTarget
|
||||||
|
for trackIdx, blk := range g.needsEnd {
|
||||||
for scene := 1; scene <= numScenes; scene++ {
|
|
||||||
cuesInScene := 1 + rng.IntN(avgCuesPerScene*2)
|
|
||||||
|
|
||||||
for intra := 1; intra <= cuesInScene; intra++ {
|
|
||||||
curCueName = fmt.Sprintf("S%d Q%d", scene, intra)
|
|
||||||
cue := &Block{
|
|
||||||
ID: curCueName,
|
|
||||||
Type: "cue",
|
|
||||||
Name: curCueName,
|
|
||||||
}
|
|
||||||
show.Blocks = append(show.Blocks, cue)
|
|
||||||
|
|
||||||
cueTargets := []TriggerTarget{}
|
|
||||||
for trackIdx, blk := range needsEnd {
|
|
||||||
hook := "END"
|
hook := "END"
|
||||||
if rng.Float64() < 0.3 {
|
if g.rng.Float64() < 0.3 {
|
||||||
hook = "FADE_OUT"
|
hook = "FADE_OUT"
|
||||||
}
|
}
|
||||||
cueTargets = append(cueTargets, TriggerTarget{Block: blk.ID, Hook: hook})
|
cueTargets = append(cueTargets, TriggerTarget{Block: blk.ID, Hook: hook})
|
||||||
chainFrom = append(chainFrom, chainable{block: blk, trackIdx: trackIdx, sameTrackOnly: true, fromEnded: true})
|
g.chainFrom = append(g.chainFrom, chainable{block: blk, trackIdx: trackIdx, sameTrackOnly: true, fromEnded: true})
|
||||||
delete(needsEnd, trackIdx)
|
delete(g.needsEnd, trackIdx)
|
||||||
}
|
}
|
||||||
|
return cueTargets
|
||||||
|
}
|
||||||
|
|
||||||
blocksThisCue := 1 + rng.IntN(avgBlocksPerCue*2)
|
func (g *mockShowGen) chainBlock(block *Block, trackIdx int, cueTargets *[]TriggerTarget) {
|
||||||
for range blocksThisCue {
|
for i, c := range g.chainFrom {
|
||||||
trackIdx := rng.IntN(numTracks)
|
|
||||||
if needsEnd[trackIdx] != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
block := randBlock(trackIdx)
|
|
||||||
show.Blocks = append(show.Blocks, block)
|
|
||||||
|
|
||||||
triggered := false
|
|
||||||
for i, c := range chainFrom {
|
|
||||||
if c.trackIdx == trackIdx {
|
if c.trackIdx == trackIdx {
|
||||||
addTrigger(
|
g.addTrigger(
|
||||||
TriggerSource{Block: c.block.ID, Signal: "END"},
|
TriggerSource{Block: c.block.ID, Signal: "END"},
|
||||||
TriggerTarget{Block: block.ID, Hook: "START"},
|
TriggerTarget{Block: block.ID, Hook: "START"},
|
||||||
)
|
)
|
||||||
chainFrom = append(chainFrom[:i], chainFrom[i+1:]...)
|
g.chainFrom = append(g.chainFrom[:i], g.chainFrom[i+1:]...)
|
||||||
triggered = true
|
return
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !triggered && rng.Float64() < 0.3 {
|
if g.rng.Float64() < 0.3 {
|
||||||
var candidates []int
|
var candidates []int
|
||||||
for i, c := range chainFrom {
|
for i, c := range g.chainFrom {
|
||||||
if !c.sameTrackOnly {
|
if !c.sameTrackOnly {
|
||||||
candidates = append(candidates, i)
|
candidates = append(candidates, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(candidates) > 0 {
|
if len(candidates) > 0 {
|
||||||
idx := candidates[rng.IntN(len(candidates))]
|
idx := candidates[g.rng.IntN(len(candidates))]
|
||||||
c := chainFrom[idx]
|
c := g.chainFrom[idx]
|
||||||
addTrigger(
|
g.addTrigger(
|
||||||
TriggerSource{Block: c.block.ID, Signal: "END"},
|
TriggerSource{Block: c.block.ID, Signal: "END"},
|
||||||
TriggerTarget{Block: block.ID, Hook: "START"},
|
TriggerTarget{Block: block.ID, Hook: "START"},
|
||||||
)
|
)
|
||||||
chainFrom = append(chainFrom[:idx], chainFrom[idx+1:]...)
|
g.chainFrom = append(g.chainFrom[:idx], g.chainFrom[idx+1:]...)
|
||||||
triggered = true
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !triggered {
|
*cueTargets = append(*cueTargets, TriggerTarget{Block: block.ID, Hook: "START"})
|
||||||
cueTargets = append(cueTargets, TriggerTarget{Block: block.ID, Hook: "START"})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !block.hasDefinedTiming() {
|
func (g *mockShowGen) promoteChainFrom() {
|
||||||
needsEnd[trackIdx] = block
|
filtered := g.chainFrom[:0]
|
||||||
} else {
|
for _, c := range g.chainFrom {
|
||||||
chainFrom = append(chainFrom, chainable{block: block, trackIdx: trackIdx, sameTrackOnly: true})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered := chainFrom[:0]
|
|
||||||
for _, c := range chainFrom {
|
|
||||||
if !c.fromEnded {
|
if !c.fromEnded {
|
||||||
c.sameTrackOnly = false
|
c.sameTrackOnly = false
|
||||||
filtered = append(filtered, c)
|
filtered = append(filtered, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chainFrom = filtered
|
g.chainFrom = filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *mockShowGen) generateCue(name string, avgBlocksPerCue int) {
|
||||||
|
g.curCueName = name
|
||||||
|
cue := &Block{
|
||||||
|
ID: name,
|
||||||
|
Type: "cue",
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
g.show.Blocks = append(g.show.Blocks, cue)
|
||||||
|
|
||||||
|
cueTargets := g.endPreviousBlocks()
|
||||||
|
|
||||||
|
blocksThisCue := 1 + g.rng.IntN(avgBlocksPerCue*2)
|
||||||
|
for range blocksThisCue {
|
||||||
|
trackIdx := g.rng.IntN(g.numTracks)
|
||||||
|
if g.needsEnd[trackIdx] != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
block := g.randBlock(trackIdx)
|
||||||
|
g.show.Blocks = append(g.show.Blocks, block)
|
||||||
|
g.chainBlock(block, trackIdx, &cueTargets)
|
||||||
|
|
||||||
|
if !block.hasDefinedTiming() {
|
||||||
|
g.needsEnd[trackIdx] = block
|
||||||
|
} else {
|
||||||
|
g.chainFrom = append(g.chainFrom, chainable{block: block, trackIdx: trackIdx, sameTrackOnly: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.promoteChainFrom()
|
||||||
|
|
||||||
if len(cueTargets) > 0 {
|
if len(cueTargets) > 0 {
|
||||||
show.Triggers = append(show.Triggers, &Trigger{
|
g.show.Triggers = append(g.show.Triggers, &Trigger{
|
||||||
Source: TriggerSource{Block: cue.ID, Signal: "GO"},
|
Source: TriggerSource{Block: cue.ID, Signal: "GO"},
|
||||||
Targets: cueTargets,
|
Targets: cueTargets,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endTargets := []TriggerTarget{}
|
func (g *mockShowGen) generateEndOfScene(scene int) {
|
||||||
for trackIdx, blk := range needsEnd {
|
var endTargets []TriggerTarget
|
||||||
|
for trackIdx, blk := range g.needsEnd {
|
||||||
hook := "END"
|
hook := "END"
|
||||||
if rng.Float64() < 0.3 {
|
if g.rng.Float64() < 0.3 {
|
||||||
hook = "FADE_OUT"
|
hook = "FADE_OUT"
|
||||||
}
|
}
|
||||||
endTargets = append(endTargets, TriggerTarget{Block: blk.ID, Hook: hook})
|
endTargets = append(endTargets, TriggerTarget{Block: blk.ID, Hook: hook})
|
||||||
delete(needsEnd, trackIdx)
|
delete(g.needsEnd, trackIdx)
|
||||||
}
|
}
|
||||||
chainFrom = nil
|
g.chainFrom = nil
|
||||||
if len(endTargets) > 0 {
|
if len(endTargets) > 0 {
|
||||||
endCueName := fmt.Sprintf("S%d End", scene)
|
endCueName := fmt.Sprintf("S%d End", scene)
|
||||||
endCue := &Block{
|
endCue := &Block{
|
||||||
@@ -207,13 +260,10 @@ func GenerateMockShow(numTracks, numScenes, avgCuesPerScene, avgBlocksPerCue int
|
|||||||
Type: "cue",
|
Type: "cue",
|
||||||
Name: endCueName,
|
Name: endCueName,
|
||||||
}
|
}
|
||||||
show.Blocks = append(show.Blocks, endCue)
|
g.show.Blocks = append(g.show.Blocks, endCue)
|
||||||
show.Triggers = append(show.Triggers, &Trigger{
|
g.show.Triggers = append(g.show.Triggers, &Trigger{
|
||||||
Source: TriggerSource{Block: endCue.ID, Signal: "GO"},
|
Source: TriggerSource{Block: endCue.ID, Signal: "GO"},
|
||||||
Targets: endTargets,
|
Targets: endTargets,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return show
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user