Skip to content

Commit

Permalink
Simplify AppLoop definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
JD557 committed Oct 7, 2023
1 parent 56ce2cc commit dbe4909
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ trait LowLevelSubsystem[Settings] extends AutoCloseable {

object LowLevelSubsystem {

type SettingsOf[Subsystem] = Subsystem match {
case LowLevelSubsystem[x] => x
}

/** Simple low-level subsystem, with basic settings.
*/
trait Simple[Settings] extends LowLevelSubsystem[Settings] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object AppLoop {
/** App loop definition that takes the initial settings, initial state
* and loop frequency.
*/
trait Definition[State, Settings, Subsystem] {
trait Definition[State, Subsystem <: LowLevelSubsystem[_]] {

/** Applies the following configuration to the app loop definition
*
Expand All @@ -45,7 +45,7 @@ object AppLoop {
* @param initialState initial state of the loop
*/
def configure(
initialSettings: Settings,
initialSettings: LowLevelSubsystem.SettingsOf[Subsystem],
frameRate: LoopFrequency,
initialState: State
): AppLoop[State, Subsystem]
Expand All @@ -56,26 +56,16 @@ object AppLoop {
* @param frameRate frame rate of the app loop
*/
def configure(
initialSettings: Settings,
initialSettings: LowLevelSubsystem.SettingsOf[Subsystem],
frameRate: LoopFrequency
)(implicit ev: Unit =:= State): AppLoop[State, Subsystem] = configure(initialSettings, frameRate, ev(()))
}

/** Creates an app loop that keeps and updates a state on every iteration,
* terminating when a certain condition is reached.
*
* This is a low level operation for custom subsystems. For most use cases,
* [[statefulRenderLoop]], [[statefulAudioLoop]] and [[statefulAppLoop]] are preferred.
*
* @param renderFrame operation to render the frame and update the state
* @param terminateWhen loop termination check
*/

def statefulLoop[State, Settings, Subsystem <: LowLevelSubsystem[Settings]](
private def fullStatefulLoop[State, Settings, Subsystem <: LowLevelSubsystem[Settings]](
renderFrame: State => Subsystem => State,
terminateWhen: State => Boolean = (_: State) => false
): AppLoop.Definition[State, Settings, Subsystem] = {
new AppLoop.Definition[State, Settings, Subsystem] {
): AppLoop.Definition[State, Subsystem] = {
new AppLoop.Definition[State, Subsystem] {
def configure(
initialSettings: Settings,
frameRate: LoopFrequency,
Expand All @@ -99,6 +89,22 @@ object AppLoop {
}
}

/** Creates an app loop that keeps and updates a state on every iteration,
* terminating when a certain condition is reached.
*
* This is a low level operation for custom subsystems. For most use cases,
* [[statefulRenderLoop]], [[statefulAudioLoop]] and [[statefulAppLoop]] are preferred.
*
* @param renderFrame operation to render the frame and update the state
* @param terminateWhen loop termination check
*/

def statefulLoop[State, Subsystem <: LowLevelSubsystem[_]](
renderFrame: State => Subsystem => State,
terminateWhen: State => Boolean = (_: State) => false
): AppLoop.Definition[State, Subsystem] =
fullStatefulLoop[State, LowLevelSubsystem.SettingsOf[Subsystem], Subsystem](renderFrame, terminateWhen)

/** Creates an app loop that keeps no state.
*
* This is a low level operation for custom subsystems. For most use cases,
Expand All @@ -107,10 +113,10 @@ object AppLoop {
* @param renderFrame operation to render the frame
* @param frameRate frame rate limit
*/
def statelessLoop[Settings, Subsystem <: LowLevelSubsystem[Settings]](
def statelessLoop[Subsystem <: LowLevelSubsystem[_]](
renderFrame: Subsystem => Unit
): AppLoop.Definition[Unit, Settings, Subsystem] =
statefulLoop[Unit, Settings, Subsystem]((_) => renderFrame)
): AppLoop.Definition[Unit, Subsystem] =
fullStatefulLoop[Unit, LowLevelSubsystem.SettingsOf[Subsystem], Subsystem]((_) => renderFrame)

/** Creates a render loop with a canvas that keeps and updates a state on every iteration,
* terminating when a certain condition is reached.
Expand All @@ -121,8 +127,8 @@ object AppLoop {
def statefulRenderLoop[State](
renderFrame: State => Canvas => State,
terminateWhen: State => Boolean = (_: State) => false
): AppLoop.Definition[State, Canvas.Settings, LowLevelCanvas] =
statefulLoop[State, Canvas.Settings, LowLevelCanvas](
): AppLoop.Definition[State, LowLevelCanvas] =
statefulLoop[State, LowLevelCanvas](
renderFrame,
terminateWhen
)
Expand All @@ -133,8 +139,8 @@ object AppLoop {
*/
def statelessRenderLoop(
renderFrame: Canvas => Unit
): AppLoop.Definition[Unit, Canvas.Settings, LowLevelCanvas] =
statelessLoop[Canvas.Settings, LowLevelCanvas](
): AppLoop.Definition[Unit, LowLevelCanvas] =
statelessLoop[LowLevelCanvas](
renderFrame
)

Expand All @@ -147,8 +153,8 @@ object AppLoop {
def statefulAudioLoop[State](
renderFrame: State => AudioPlayer => State,
terminateWhen: State => Boolean = (_: State) => false
): AppLoop.Definition[State, AudioPlayer.Settings, LowLevelAudioPlayer] =
statefulLoop[State, AudioPlayer.Settings, LowLevelAudioPlayer](
): AppLoop.Definition[State, LowLevelAudioPlayer] =
statefulLoop[State, LowLevelAudioPlayer](
renderFrame,
terminateWhen
)
Expand All @@ -159,8 +165,8 @@ object AppLoop {
*/
def statelessAudioLoop(
renderFrame: AudioPlayer => Unit
): AppLoop.Definition[Unit, AudioPlayer.Settings, LowLevelAudioPlayer] =
statelessLoop[AudioPlayer.Settings, LowLevelAudioPlayer](
): AppLoop.Definition[Unit, LowLevelAudioPlayer] =
statelessLoop[LowLevelAudioPlayer](
renderFrame
)

Expand All @@ -176,8 +182,8 @@ object AppLoop {
def statefulAppLoop[State](
renderFrame: State => (Canvas with AudioPlayer) => State,
terminateWhen: State => Boolean = (_: State) => false
): AppLoop.Definition[State, (Canvas.Settings, AudioPlayer.Settings), LowLevelAllSubsystems] =
statefulLoop[State, (Canvas.Settings, AudioPlayer.Settings), LowLevelAllSubsystems](
): AppLoop.Definition[State, LowLevelAllSubsystems] =
statefulLoop[State, LowLevelAllSubsystems](
(state: State) => { case LowLevelSubsystem.Composite(canvas, audioPlayer) =>
renderFrame(state)(new AllSubsystems(canvas, audioPlayer))
},
Expand All @@ -190,8 +196,8 @@ object AppLoop {
*/
def statelessAppLoop(
renderFrame: (Canvas with AudioPlayer) => Unit
): AppLoop.Definition[Unit, (Canvas.Settings, AudioPlayer.Settings), LowLevelAllSubsystems] =
statelessLoop[(Canvas.Settings, AudioPlayer.Settings), LowLevelAllSubsystems](
): AppLoop.Definition[Unit, LowLevelAllSubsystems] =
statelessLoop[LowLevelAllSubsystems](
{ case LowLevelSubsystem.Composite(canvas, audioPlayer) => renderFrame(new AllSubsystems(canvas, audioPlayer)) }
)

Expand Down

0 comments on commit dbe4909

Please sign in to comment.