Skip to content

Commit

Permalink
looper controls updated to have mode segmented button and Init; added…
Browse files Browse the repository at this point in the history
… OnInit to Stack -- called by Init button and function.
  • Loading branch information
rcoreilly committed Nov 5, 2024
1 parent c568961 commit 35a3b6d
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 147 deletions.
204 changes: 129 additions & 75 deletions egui/loopctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
package egui

import (
"cmp"
"slices"

"cogentcore.org/core/core"
"cogentcore.org/core/enums"
"cogentcore.org/core/events"
"cogentcore.org/core/icons"
"cogentcore.org/core/styles"
Expand All @@ -15,16 +19,72 @@ import (
"github.com/emer/emergent/v2/looper"
)

// AddLooperCtrl adds toolbar control for looper.Stack
// with Run, Step controls.
func (gui *GUI) AddLooperCtrl(p *tree.Plan, loops *looper.Manager, modes []etime.Modes, prefix ...string) {
pfx := ""
if len(prefix) == 1 {
pfx = prefix[0] + ": "
// AddLooperCtrl adds toolbar control for looper.Stacks with Init, Run, Step controls,
// with selector for which stack is being controlled.
func (gui *GUI) AddLooperCtrl(p *tree.Plan, loops *looper.Stacks) {
modes := make([]enums.Enum, len(loops.Stacks))
var stepChoose *core.Chooser
var stepNSpin *core.Spinner
i := 0
for m := range loops.Stacks {
modes[i] = m
i++
}
slices.SortFunc(modes, func(a, b enums.Enum) int {
return cmp.Compare(a.Int64(), b.Int64())
})
curMode := modes[0]
curStep := loops.Stacks[curMode].StepLevel

updateSteps := func() {
st := loops.Stacks[curMode]
stepStrs := make([]string, len(st.Order))
cur := ""
for i, s := range st.Order {
sv := s.String()
stepStrs[i] = sv
if s.Int64() == curStep.Int64() {
cur = sv
}
}
stepChoose.SetStrings(stepStrs...)
stepChoose.SetCurrentValue(cur)
}
gui.AddToolbarItem(p, ToolbarItem{Label: pfx + "Stop",

tree.AddAt(p, "loop-mode", func(w *core.Switches) {
w.SetType(core.SwitchSegmentedButton)
w.Mutex = true
w.SetEnums(modes...)
w.SelectValue(curMode)
w.FinalStyler(func(s *styles.Style) {
s.Grow.Set(0, 0)
})
w.OnChange(func(e events.Event) {
curMode = w.SelectedItem().Value.(enums.Enum)
st := loops.Stacks[curMode]
if st != nil {
curStep = st.StepLevel
}
updateSteps()
stepChoose.Update()
stepN := st.Loops[curStep].StepCount
stepNSpin.SetValue(float32(stepN))
stepNSpin.Update()
})
})

gui.AddToolbarItem(p, ToolbarItem{Label: "Init",
Icon: icons.Update,
Tooltip: "Initializes running and state for current mode.",
Active: ActiveStopped,
Func: func() {
loops.InitMode(curMode)
},
})

gui.AddToolbarItem(p, ToolbarItem{Label: "Stop",
Icon: icons.Stop,
Tooltip: "Interrupts running. running / stepping picks back up where it left off.",
Tooltip: "Interrupts current running. Will pick back up where it left off.",
Active: ActiveRunning,
Func: func() {
loops.Stop(etime.Cycle)
Expand All @@ -33,80 +93,74 @@ func (gui *GUI) AddLooperCtrl(p *tree.Plan, loops *looper.Manager, modes []etime
},
})

for _, m := range modes {
mode := m
tree.AddAt(p, pfx+mode.String()+"-run", func(w *core.Button) {
tb := gui.Toolbar
w.SetText(pfx + mode.String() + " Run").SetIcon(icons.PlayArrow).
SetTooltip("Run the " + pfx + mode.String() + " process")
w.FirstStyler(func(s *styles.Style) { s.SetEnabled(!gui.IsRunning) })
w.OnClick(func(e events.Event) {
if !gui.IsRunning {
gui.IsRunning = true
tb.Restyle()
go func() {
loops.Run(mode)
gui.Stopped()
}()
}
})
tree.AddAt(p, "loop-run", func(w *core.Button) {
tb := gui.Toolbar
w.SetText("Run").SetIcon(icons.PlayArrow).
SetTooltip("Run the current mode, picking up from where it left off last time (Init to restart)")
w.FirstStyler(func(s *styles.Style) { s.SetEnabled(!gui.IsRunning) })
w.OnClick(func(e events.Event) {
if !gui.IsRunning {
gui.IsRunning = true
tb.Restyle()
go func() {
loops.Run(curMode)
gui.Stopped()
}()
}
})
})

stepN := make(map[string]int)
steps := loops.Stacks[mode].Order
stringToEnumTime := make(map[string]etime.Times)
for _, st := range steps {
stepN[st.String()] = 1
stringToEnumTime[st.String()] = st
}
tree.AddAt(p, "loop-step", func(w *core.Button) {
tb := gui.Toolbar
w.SetText("Step").SetIcon(icons.SkipNext).
SetTooltip("Step the current mode, according to the following step level and N")
w.FirstStyler(func(s *styles.Style) {
s.SetEnabled(!gui.IsRunning)
s.SetAbilities(true, abilities.RepeatClickable)
})
w.OnClick(func(e events.Event) {
if !gui.IsRunning {
gui.IsRunning = true
tb.Restyle()
go func() {
st := loops.Stacks[curMode]
nst := int(stepNSpin.Value)
loops.Step(curMode, nst, st.StepLevel)
gui.Stopped()
}()
}
})
})

tree.AddAt(p, pfx+mode.String()+"-step", func(w *core.Button) {
tb := gui.Toolbar
w.SetText(pfx + "Step").SetIcon(icons.SkipNext).
SetTooltip("Step the " + pfx + mode.String() + " process according to the following step level and N")
w.FirstStyler(func(s *styles.Style) {
s.SetEnabled(!gui.IsRunning)
s.SetAbilities(true, abilities.RepeatClickable)
})
w.OnClick(func(e events.Event) {
if !gui.IsRunning {
gui.IsRunning = true
tb.Restyle()
go func() {
stack := loops.Stacks[mode]
loops.Step(mode, stepN[stack.StepLevel.String()], stack.StepLevel)
gui.Stopped()
}()
tree.AddAt(p, "step-level", func(w *core.Chooser) {
stepChoose = w
updateSteps()
w.SetCurrentValue(curStep.String())
w.OnChange(func(e events.Event) {
st := loops.Stacks[curMode]
cs := stepChoose.CurrentItem.Value.(string)
for _, l := range st.Order {
if l.String() == cs {
st.StepLevel = l
stepNSpin.Value = float32(st.Loops[l].StepCount)
stepNSpin.Update()
break
}
})
}
})
})

var chs *core.Chooser
var sp *core.Spinner
tree.AddAt(p, pfx+mode.String()+"-level", func(w *core.Chooser) {
chs = w
stepStrs := []string{}
for _, s := range steps {
stepStrs = append(stepStrs, s.String())
tree.AddAt(p, "step-n", func(w *core.Spinner) {
stepNSpin = w
w.SetStep(1).SetMin(1).SetValue(1)
w.SetTooltip("number of iterations per step")
w.OnChange(func(e events.Event) {
st := loops.Stacks[curMode]
if st != nil {
st.StepCount = int(w.Value)
st.Loops[st.StepLevel].StepCount = st.StepCount
}
w.SetStrings(stepStrs...)
stack := loops.Stacks[mode]
w.SetCurrentValue(stack.StepLevel.String())
w.OnChange(func(e events.Event) {
stack := loops.Stacks[mode]
stack.StepLevel = stringToEnumTime[chs.CurrentItem.Value.(string)]
sp.Value = float32(stepN[stack.StepLevel.String()])
sp.Update()
})
})
})

tree.AddAt(p, pfx+mode.String()+"-n", func(w *core.Spinner) {
sp = w
w.SetStep(1).SetMin(1).SetValue(1)
w.SetTooltip("number of iterations per step")
w.OnChange(func(e events.Event) {
stepN[chs.CurrentItem.Value.(string)] = int(w.Value)
})
})
}
}
28 changes: 24 additions & 4 deletions looper/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,43 @@ type Loop struct {
// IsDone functions are called after each loop iteration,
// and if any return true, then the loop iteration is terminated.
IsDone NamedFuncs

// StepCount is the default step count for this loop level.
StepCount int
}

// NewLoop returns a new loop with given Counter Max and increment.
func NewLoop(ctrMax, ctrIncr int) *Loop {
lp := &Loop{}
lp.Counter.SetMaxInc(ctrMax, ctrIncr)
lp.StepCount = 1
return lp
}

// AddEvent adds a new event at given counter.
// AddEvent adds a new event at given counter. If an event already exists
// for that counter, the function is added to the list for that event.
func (lp *Loop) AddEvent(name string, atCtr int, fun func()) *Event {
ev := NewEvent(name, atCtr, fun)
lp.Events = append(lp.Events, ev)
ev := lp.EventByCounter(atCtr)
if ev == nil {
ev = NewEvent(name, atCtr, fun)
lp.Events = append(lp.Events, ev)
} else {
ev.OnEvent.Add(name, fun)
}
return ev
}

// EventByName returns event by name, nil if not found
// EventByCounter returns event for given atCounter value, nil if not found.
func (lp *Loop) EventByCounter(atCtr int) *Event {
for _, ev := range lp.Events {
if ev.AtCounter == atCtr {
return ev
}
}
return nil
}

// EventByName returns event by name, nil if not found.
func (lp *Loop) EventByName(name string) *Event {
for _, ev := range lp.Events {
if ev.Name == name {
Expand Down
50 changes: 41 additions & 9 deletions looper/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ type Stack struct {
// the beginning and shorter timescales like Trial should be and the end.
Order []enums.Enum

// If true, stop model at the end of the current StopLevel.
// OnInit are functions to run for Init function of this stack,
// which also resets the counters for this stack.
OnInit NamedFuncs

// StopNext will stop running at the end of the current StopLevel if set.
StopNext bool

// StopFlag will stop running ASAP if set.
Expand All @@ -53,13 +57,13 @@ type Stack struct {

// NewStack returns a new Stack for given mode.
func NewStack(mode enums.Enum) *Stack {
stack := &Stack{}
stack.Init(mode)
return stack
st := &Stack{}
st.newInit(mode)
return st
}

// Init initializes new data structures for a newly created object
func (st *Stack) Init(mode enums.Enum) {
// newInit initializes new data structures for a newly created object.
func (st *Stack) newInit(mode enums.Enum) {
st.Mode = mode
st.StepLevel = etime.Trial
st.StepCount = 1
Expand All @@ -83,6 +87,24 @@ func (st *Stack) AddTime(time enums.Enum, ctrMax int) *Stack {
return st
}

// AddOnStartToAll adds given function taking mode and time args to OnStart in all loops.
func (st *Stack) AddOnStartToAll(name string, fun func(mode, time enums.Enum)) {
for tt, lp := range st.Loops {
lp.OnStart.Add(name, func() {
fun(st.Mode, tt)
})
}
}

// AddOnEndToAll adds given function taking mode and time args to OnEnd in all loops.
func (st *Stack) AddOnEndToAll(name string, fun func(mode, time enums.Enum)) {
for tt, lp := range st.Loops {
lp.OnEnd.Add(name, func() {
fun(st.Mode, tt)
})
}
}

// AddTimeIncr adds a new timescale to this Stack with a given ctrMax number of iterations,
// and increment per step.
// The order in which this method is invoked is important,
Expand Down Expand Up @@ -117,11 +139,21 @@ func (st *Stack) TimeBelow(time enums.Enum) enums.Enum {
return etime.NoTime
}

//////// Control

// SetStep sets stepping to given level and number of iterations.
func (st *Stack) SetStep(numSteps int, stopscale enums.Enum) {
st.StopLevel = stopscale
// If numSteps == 0 then the default for the given stops
func (st *Stack) SetStep(numSteps int, stopLevel enums.Enum) {
st.StopLevel = stopLevel
lp := st.Loops[stopLevel]
if numSteps > 0 {
st.StopCount = numSteps
lp.StepCount = numSteps
} else {
numSteps = lp.StepCount
}
st.StopCount = numSteps
st.StepLevel = stopscale
st.StepLevel = stopLevel
st.StepCount = numSteps
st.StopFlag = false
st.StopNext = true
Expand Down
Loading

0 comments on commit 35a3b6d

Please sign in to comment.