From 4461b4f7274177b895a1fcf93ef1df56e403353c Mon Sep 17 00:00:00 2001 From: mdgriffith Date: Mon, 25 May 2020 09:26:42 -0400 Subject: [PATCH] queueing events should create a new `Line` to prevent awkward variation based on when the queue occurs --- src/Internal/Timeline.elm | 213 ++++++++++++------------ tests/CSS.elm | 2 +- tests/Fuzz/Timeline.elm | 2 +- tests/Interpolation.elm | 8 +- tests/Scheduling.elm | 329 ++++++++++++++++++++++++++++++-------- 5 files changed, 373 insertions(+), 181 deletions(-) diff --git a/src/Internal/Timeline.elm b/src/Internal/Timeline.elm index d99c0db..42c244c 100644 --- a/src/Internal/Timeline.elm +++ b/src/Internal/Timeline.elm @@ -8,7 +8,7 @@ module Internal.Timeline exposing , current , Line(..), Timetable(..) , foldp, capture, captureTimeline - , ActualDuration(..), Animator(..), Description(..), Frame(..), Frames(..), FramesSummary, Interp, LookAhead, Oscillator(..), Pause(..), Period(..), Previous(..), Resting(..), Summary, SummaryEvent(..), atTime, gc, hasChanged, justInitialized, linesAreActive, prepareOscillator, previous, previousEndTime, previousStartTime, updateNoGC + , ActualDuration(..), Animator(..), Description(..), Frame(..), Frames(..), FramesSummary, Interp, LookAhead, Oscillator(..), Pause(..), Period(..), Previous(..), Resting(..), Summary, SummaryEvent(..), atTime, gc, hasChanged, justInitialized, linesAreActive, prepareOscillator, previous, previousEndTime, previousStartTime, updateWith ) {-| @@ -351,9 +351,14 @@ atTime now (Timeline timeline) = Timeline { timeline | now = Time.absolute now } -{-| -} update : Time.Posix -> Timeline event -> Timeline event -update possiblyNow (Timeline timeline) = +update = + updateWith True + + +{-| -} +updateWith : Bool -> Time.Posix -> Timeline event -> Timeline event +updateWith withGC possiblyNow (Timeline timeline) = let -- we can only move forward with updating -- This is so that the Animator event "GC" doesn't cause awkward skips. @@ -376,45 +381,14 @@ update possiblyNow (Timeline timeline) = } |> applyQueued |> applyInterruptions - |> clean True + |> clean withGC |> Timeline else { timeline | now = now } |> applyQueued |> applyInterruptions - |> clean True - |> Timeline - - -updateNoGC : Time.Posix -> Timeline event -> Timeline event -updateNoGC now (Timeline timeline) = - let - absoluteNow = - Time.absolute now - in - if timeline.events == Timetable [] then - { timeline - | now = absoluteNow - , events = - let - firstOccurring = - Occurring timeline.initial absoluteNow absoluteNow - in - Timetable - [ Line absoluteNow firstOccurring [] - ] - } - |> applyQueued - |> applyInterruptions - |> clean False - |> Timeline - - else - { timeline | now = absoluteNow } - |> applyQueued - |> applyInterruptions - |> clean False + |> clean withGC |> Timeline @@ -795,25 +769,22 @@ interruptLine startInterruption scheduled line future = Nothing -getTransitionAt interruptionTime startEvent trailing = +{-| This will provide the two events we are currently between. +-} +getTransitionAt interruptionTime prev trailing = case trailing of [] -> Nothing next :: remain -> - let - prev = - List.head remain - |> Maybe.withDefault startEvent - in if Time.thisAfterOrEqualThat interruptionTime (endTime prev) && Time.thisBeforeThat interruptionTime (startTime next) then Just (LastTwoEvents (endTime prev) (getEvent prev) (startTime next) (getEvent next)) else - getTransitionAt interruptionTime startEvent remain + getTransitionAt interruptionTime next remain -interruptAtExactly startInterruption scheduled (LastTwoEvents penultimateTime penultimate lastEventTime lastEvent) = +interruptAtExactly startInterruption scheduled ((LastTwoEvents penultimateTime penultimate lastEventTime lastEvent) as last) = case scheduled of Schedule delay_ startingEvent reverseQueued -> let @@ -821,6 +792,7 @@ interruptAtExactly startInterruption scheduled (LastTwoEvents penultimateTime pe Time.progress penultimateTime lastEventTime startInterruption newStartingEvent = + -- we apply the discount if we are returning to a state if penultimate == getScheduledEvent startingEvent then startingEvent |> adjustScheduledDuration (Quantity.multiplyBy amountProgress) @@ -850,36 +822,59 @@ enqueue timeline now scheduled = Timetable (addToCurrentLine now scheduled lines) -addToCurrentLine : Time.Absolute -> Schedule event -> List (Line event) -> List (Line event) -addToCurrentLine now scheduled lines = - let - onCurrent timelines = - case timelines of - [] -> - [ createLine now scheduled ] +{-| There's some nuance to when we can add events to a `Line`. - (Line startOne startEvent one) :: [] -> - -- if we've gotten here, this line is current - [ addEventsToLine now scheduled (Line startOne startEvent one) ] +When interpolating we allow the interpolator to look ahead one event in order to calculate the desired velocity it should be at. - (Line startOne startEventOne one) :: (Line startTwo startEventTwo two) :: remaining -> - -- we check if now is after startOne, but before startTwo - if Time.thisAfterOrEqualThat now startOne && Time.thisAfterOrEqualThat startTwo now then - -- one is the current timeline - addEventsToLine now scheduled (Line startOne startEventOne one) - :: Line startTwo startEventTwo two - :: remaining +This lookahead only happens within Lines, which means we can only append to the current line if appending it would be after the event that we're using fro that calculation. - else - -- need to search farther. - Line startOne startEventOne one - :: onCurrent (Line startTwo startEventTwo two :: remaining) - in - onCurrent lines +e.g. + + a------------b---------c-------d + ^ now ^---------^ these two events are used to calculate the desired velocity + +So, if we have the above situation, then we could append to this line. + +However, the below situation, we shouldnt. + + a-----------b---------c-------d + ^ now + +**However!** **Both queueing and interruptions should create a new \`Line** + + - This is to ensure that there is not retroactive effect. + - Also! If we're conditionally changing a `Line` via queueing, it means the animation will be different depending on the timing of when the queueing happens! Oof. What if the player in a game is mashing buttons and animations change intermittently? No Bueno. + +-} +addToCurrentLine : Time.Absolute -> Schedule event -> List (Line event) -> List (Line event) +addToCurrentLine now scheduled lines = + case lines of + [] -> + [ createLine now scheduled ] + + line :: [] -> + -- if we've gotten here, this line is current + addEventsToLine now scheduled line [] + + (Line startOne startEventOne one) :: (Line startTwo startEventTwo two) :: remaining -> + -- we check if now is after startOne, but before startTwo + if Time.thisAfterOrEqualThat now startOne && Time.thisBeforeOrEqualThat now startTwo then + -- one is the current timeline + addEventsToLine now + scheduled + (Line startOne startEventOne one) + (Line startTwo startEventTwo two + :: remaining + ) + + else + -- need to search farther. + Line startOne startEventOne one + :: addToCurrentLine now scheduled (Line startTwo startEventTwo two :: remaining) createLine : Time.Absolute -> Schedule events -> Line events -createLine now (Schedule _ (Event dur startEvent maybeDwell) reverseQueued) = +createLine now ((Schedule _ (Event dur startEvent maybeDwell) reverseQueued) as scheduled) = let start = Time.advanceBy dur now @@ -893,31 +888,35 @@ createLine now (Schedule _ (Event dur startEvent maybeDwell) reverseQueued) = Time.advanceBy dwell start events = - List.foldl toOccurring ( startNextEvent, [] ) (List.reverse reverseQueued) + List.reverse reverseQueued + |> List.foldl toOccurring ( startNextEvent, [] ) |> Tuple.second |> List.reverse in Line now (Occurring startEvent start startNextEvent) events -addEventsToLine : Time.Absolute -> Schedule events -> Line events -> Line events -addEventsToLine now (Schedule delay scheduledStartingEvent reverseQueued) (Line startLineAt startingEvent events) = - let - queued = - scheduledStartingEvent :: List.reverse reverseQueued +{-| Given our explanation above, this function does the following - reversedEvents = - List.reverse events + 1. modifies the last event of the existing line as necessary + 2. creates a new line representing the queueing. +-} +addEventsToLine : Time.Absolute -> Schedule events -> Line events -> List (Line events) -> List (Line events) +addEventsToLine now ((Schedule delay scheduledStartingEvent reverseQueued) as scheduled) ((Line startLineAt startingEvent events) as existing) lines = + let start = Time.advanceBy delay now in - case reversedEvents of + case List.reverse events of [] -> let startNewEventsAt = Time.latest (Time.advanceBy delay (endTime startingEvent)) start + newLine = + createLine startNewEventsAt scheduled + startingEventWithDwell = case startingEvent of Occurring ev lastEventTime _ -> @@ -927,24 +926,19 @@ addEventsToLine now (Schedule delay scheduledStartingEvent reverseQueued) (Line else Occurring ev lastEventTime lastEventTime in - List.foldl toOccurring ( startNewEventsAt, [] ) queued - |> Tuple.second - |> List.reverse - |> Line startLineAt startingEventWithDwell + Line startLineAt startingEventWithDwell [] :: newLine :: lines (Occurring lastEvent lastEventTime lastEventFinish) :: eventTail -> let startNewEventsAt = Time.latest (Time.advanceBy delay lastEventFinish) start - newEvents = - List.foldl toOccurring ( startNewEventsAt, [] ) queued - |> Tuple.second - |> List.reverse - in - -- we need to increase the dwell time of the last event - -- to match the start time of the new queued events. - let + newLine = + createLine startNewEventsAt scheduled + + -- we need to increase the dwell time of the last event + -- to match the start time of the new queued events. + -- let newLastEvent = Occurring lastEvent lastEventTime @@ -952,9 +946,9 @@ addEventsToLine now (Schedule delay scheduledStartingEvent reverseQueued) (Line in Line startLineAt startingEvent - (List.reverse (newLastEvent :: eventTail) - ++ newEvents - ) + (List.reverse (newLastEvent :: eventTail)) + :: newLine + :: lines toOccurring : Event event -> ( Time.Absolute, List (Occurring event) ) -> ( Time.Absolute, List (Occurring event) ) @@ -971,17 +965,7 @@ toOccurring (Event duration event maybeDwell) ( now, events ) = Just dwell -> Time.advanceBy dwell occursAt in - case events of - [] -> - ( endsAt, [ Occurring event occursAt endsAt ] ) - - prev :: remain -> - if startTime prev == occursAt then - -- then this event would supercede the previous one - ( endsAt, Occurring event occursAt endsAt :: remain ) - - else - ( endsAt, Occurring event occursAt endsAt :: events ) + ( endsAt, Occurring event occursAt endsAt :: events ) addToDwell : Time.Duration -> Maybe Time.Duration -> Maybe Time.Duration @@ -1127,7 +1111,7 @@ overLines fn lookup details maybePreviousEvent (Line lineStart lineStartEv lineR { anchor = lookup (getEvent upcoming) , time = Time.inMilliseconds (startTimeAdj lookup fn.adjustor lineStartEv upcoming) , resting = - not (hasDwell upcoming) + hasDwell upcoming } ) state @@ -1143,7 +1127,7 @@ overLines fn lookup details maybePreviousEvent (Line lineStart lineStartEv lineR upcoming :: _ -> endTimeAdj lookup fn.adjustor lineStartEv upcoming in - if Time.thisAfterOrEqualThat now eventEndTime then + if Time.thisAfterThat now eventEndTime then -- after linestartEv case lineRemain of [] -> @@ -1184,14 +1168,14 @@ overLines fn lookup details maybePreviousEvent (Line lineStart lineStartEv lineR { anchor = lookup (getEvent upcoming) , time = Time.inMilliseconds (startTimeAdj lookup fn.adjustor next upcoming) , resting = - not (hasDwell upcoming) + hasDwell upcoming } ) (if hasDwell lineStartEv then fn.dwellFor (lookup (getEvent lineStartEv)) (Time.duration eventStartTime eventEndTime) else - state + fn.after lookup lineStartEv lineRemain ) |> transition @@ -1258,7 +1242,7 @@ overLines fn lookup details maybePreviousEvent (Line lineStart lineStartEv lineR { anchor = lookup (getEvent upcoming) , time = Time.inMilliseconds (startTimeAdj lookup fn.adjustor next2 upcoming) , resting = - not (hasDwell upcoming) + hasDwell upcoming } ) after @@ -1293,13 +1277,20 @@ overLines fn lookup details maybePreviousEvent (Line lineStart lineStartEv lineR after else - -- dwell at linestartEv - -- we've checked that it's not after or before, - -- so it has to be between the start and end + -- dwell at linestartEv + -- we've checked that it's not after or before, + -- so it has to be between the start and end + if + hasDwell lineStartEv + then fn.dwellFor (lookup (getEvent lineStartEv)) (Time.duration eventStartTime now) |> transition + else + fn.after lookup lineStartEv lineRemain + |> transition + type alias FramesSummary motion = { frames : List (Frame motion) diff --git a/tests/CSS.elm b/tests/CSS.elm index 7537ce8..f1c164f 100644 --- a/tests/CSS.elm +++ b/tests/CSS.elm @@ -185,7 +185,7 @@ frames = -- scheduling an event |> Timeline.update (Time.millisToPosix 1) |> Animator.go (Animator.seconds 1) Three - |> Timeline.updateNoGC (Time.millisToPosix 500) + |> Timeline.updateWith False (Time.millisToPosix 500) resultFrames = Timeline.capture 60 toVals Interpolate.linearly timeline diff --git a/tests/Fuzz/Timeline.elm b/tests/Fuzz/Timeline.elm index 1369d15..8e2bba6 100644 --- a/tests/Fuzz/Timeline.elm +++ b/tests/Fuzz/Timeline.elm @@ -95,7 +95,7 @@ toTimeline { gc } (InstructionTimeline startTime startEvent instructions) = Timeline.update x else - Timeline.updateNoGC x + Timeline.updateWith False x addInstructions myTimeline = List.foldl instruct myTimeline instructions diff --git a/tests/Interpolation.elm b/tests/Interpolation.elm index e13c35f..a91dcd9 100644 --- a/tests/Interpolation.elm +++ b/tests/Interpolation.elm @@ -222,17 +222,17 @@ timeline = let doubleEvent = Animator.init Hufflepuff - |> Timeline.update (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 0) |> Animator.queue [ Animator.wait (Animator.seconds 1) , Animator.event (Animator.seconds 1) Griffyndor ] - |> Timeline.update (Time.millisToPosix 1000) + |> Timeline.updateWith False (Time.millisToPosix 1000) |> Animator.interrupt [ Animator.event (Animator.seconds 1) Ravenclaw ] - |> Timeline.update (Time.millisToPosix 2500) - |> Timeline.update (Time.millisToPosix 3000) + |> Timeline.updateWith False (Time.millisToPosix 2500) + |> Timeline.updateWith False (Time.millisToPosix 3000) position = Interpolate.details diff --git a/tests/Scheduling.elm b/tests/Scheduling.elm index 4ebfa05..522272e 100644 --- a/tests/Scheduling.elm +++ b/tests/Scheduling.elm @@ -58,6 +58,37 @@ toVals event = Animator.at -1 +toOscVals event = + case event of + Starting -> + Animator.wave 0 1 + |> Animator.loop (Animator.millis 100) + + One -> + Animator.wave 1 2 + |> Animator.loop (Animator.millis 100) + + Two -> + Animator.wave 2 3 + |> Animator.loop (Animator.millis 100) + + Three -> + Animator.wave 3 4 + |> Animator.loop (Animator.millis 100) + + Four -> + Animator.wave 4 5 + |> Animator.loop (Animator.millis 100) + + Five -> + Animator.wave 5 6 + |> Animator.loop (Animator.millis 100) + + Unreachable -> + Animator.wave -1 0 + |> Animator.loop (Animator.millis 100) + + valueAtEquals time val tl = let new = @@ -70,6 +101,18 @@ valueAtEquals time val tl = val +valueOscAtEquals time val tl = + let + new = + -- we can't call update because updates have to be monotonic + Timeline.atTime (Time.millisToPosix time) tl + in + Expect.within + (Absolute 0.001) + (Animator.move new toOscVals) + val + + timeline = Animator.init Starting |> Timeline.update (Time.millisToPosix 0) @@ -101,8 +144,10 @@ queueing = Timeline.Timetable [ Timeline.Line (qty 0) (occur Starting (qty 0) (qty 1000)) - [ occur One (qty 2000) (qty 3000) - , occur Two (qty 4000) (qty 5000) + [] + , Timeline.Line (qty 1000) + (occur One (qty 2000) (qty 3000)) + [ occur Two (qty 4000) (qty 5000) , occur Three (qty 6000) (qty 7000) ] ] @@ -122,6 +167,48 @@ queueing = , valueAtEquals 6000 3 ] newTimeline + , test "Values are consistent right after event" <| + \_ -> + let + tl = + Animator.init Starting + |> Timeline.update (Time.millisToPosix 0) + |> Animator.queue + [ Animator.event (Animator.seconds 1) One + , Animator.event (Animator.seconds 1) Two + , Animator.event (Animator.seconds 1) Three + ] + |> Timeline.update (Time.millisToPosix 0) + + -- x = + -- Timeline + -- { events = + -- Timetable + -- [ Line (Quantity 0) + -- (Occurring Starting (Quantity 0) (Quantity 0)) + -- [] + -- , Line (Quantity 0) + -- (Occurring One (Quantity 1000) (Quantity 1000)) + -- [ Occurring Two (Quantity 2000) (Quantity 2000) + -- , Occurring Three (Quantity 3000) (Quantity 3000) + -- ] + -- ] + -- , initial = Starting + -- , interruption = [] + -- , now = Quantity 0 + -- , queued = Nothing + -- , running = True + -- } + in + Expect.all + [ -- valueAtEquals 0 0 + valueOscAtEquals 999 1 + + -- valueAtEquals 1001 1 + -- , valueAtEquals 2000 2 + -- , valueAtEquals 3000 3 + ] + tl ] , describe "Queued in ongoing" [ test "multiple events queued" <| @@ -139,8 +226,8 @@ queueing = , Animator.event (Animator.seconds 1) Three , Animator.wait (Animator.seconds 1.0) ] - |> Timeline.updateNoGC (Time.millisToPosix 0) - |> Timeline.updateNoGC (Time.millisToPosix 3000) + |> Timeline.updateWith False (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 3000) |> Animator.queue [ Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) One @@ -150,8 +237,7 @@ queueing = , Animator.event (Animator.seconds 1) Three , Animator.wait (Animator.seconds 1.0) ] - |> Debug.log "queued" - |> Timeline.updateNoGC (Time.millisToPosix 4000) + |> Timeline.updateWith False (Time.millisToPosix 4000) in Expect.equal queuedTimeline @@ -160,13 +246,36 @@ queueing = Timeline.Timetable [ Timeline.Line (qty 0) (occur Starting (qty 0) (qty 1000)) - [ occur One (qty 2000) (qty 3000) - , occur Two (qty 4000) (qty 5000) + [] + , Timeline.Line (qty 1000) + (occur One (qty 2000) (qty 3000)) + [ occur Two (qty 4000) (qty 5000) , occur Three (qty 6000) (qty 8000) - , occur One (qty 9000) (qty 10000) - , occur Two (qty 11000) (qty 12000) + ] + , Timeline.Line (qty 8000) + (occur One (qty 9000) (qty 10000)) + [ occur Two (qty 11000) (qty 12000) , occur Three (qty 13000) (qty 14000) ] + + -- , Timeline.Line (qty 0) + -- (occur Starting (qty 0) (qty 1000)) + -- [ occur One (qty 2000) (qty 3000) + -- , occur Two (qty 4000) (qty 5000) + -- , occur Three (qty 6000) (qty 8000) + -- , occur One (qty 9000) (qty 10000) + -- , occur Two (qty 11000) (qty 12000) + -- , occur Three (qty 13000) (qty 14000) + -- ] + -- , Timeline.Line (qty 0) + -- (occur Starting (qty 0) (qty 1000)) + -- [ occur One (qty 2000) (qty 3000) + -- , occur Two (qty 4000) (qty 5000) + -- , occur Three (qty 6000) (qty 8000) + -- , occur One (qty 9000) (qty 10000) + -- , occur Two (qty 11000) (qty 12000) + -- , occur Three (qty 13000) (qty 14000) + -- ] ] , initial = Starting , interruption = [] @@ -181,15 +290,15 @@ queueing = queuedTimeline = Animator.init Starting |> Animator.go (Animator.seconds 2) One - |> Timeline.updateNoGC (Time.millisToPosix 0) - |> Timeline.updateNoGC (Time.millisToPosix 100) - |> Timeline.updateNoGC (Time.millisToPosix 200) + |> Timeline.updateWith False (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 100) + |> Timeline.updateWith False (Time.millisToPosix 200) |> Animator.queue [ Animator.event (Animator.seconds 1) Starting , Animator.wait (Animator.seconds 5) , Animator.event (Animator.seconds 2) One ] - |> Timeline.updateNoGC (Time.millisToPosix 300) + |> Timeline.updateWith False (Time.millisToPosix 300) in Expect.equal queuedTimeline @@ -201,8 +310,10 @@ queueing = [] , Timeline.Line (qty 0) (occur One (qty 2000) (qty 2000)) - [ occur Starting (qty 3000) (qty 8000) - , occur One (qty 10000) (qty 10000) + [] + , Timeline.Line (qty 2000) + (occur Starting (qty 3000) (qty 8000)) + [ occur One (qty 10000) (qty 10000) ] ] , initial = Starting @@ -212,40 +323,39 @@ queueing = , running = True } ) - , only <| - test "Queueing events does not change the current value" <| - \_ -> - let - currentTimeline = - Animator.init Starting - |> Animator.go (Animator.seconds 2) One - |> Timeline.updateNoGC (Time.millisToPosix 0) - |> Timeline.updateNoGC (Time.millisToPosix 100) - |> Timeline.updateNoGC (Time.millisToPosix 200) - - queuedTimeline = - currentTimeline - |> Animator.queue - [ Animator.event (Animator.seconds 1) Starting - , Animator.wait (Animator.seconds 5) - , Animator.event (Animator.seconds 2) One - ] - |> Timeline.updateNoGC (Time.millisToPosix 300) - - current = - Animator.move - (Timeline.atTime (Time.millisToPosix 300) currentTimeline) - toVals - - queued = - Animator.move - (Timeline.atTime (Time.millisToPosix 300) queuedTimeline) - toVals - in - Expect.within - (Absolute 0.001) - current - queued + , test "Queueing events does not change the current value" <| + \_ -> + let + currentTimeline = + Animator.init Starting + |> Animator.go (Animator.seconds 2) One + |> Timeline.updateWith False (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 100) + |> Timeline.updateWith False (Time.millisToPosix 200) + + queuedTimeline = + currentTimeline + |> Animator.queue + [ Animator.event (Animator.seconds 1) Starting + , Animator.wait (Animator.seconds 5) + , Animator.event (Animator.seconds 2) One + ] + |> Timeline.updateWith False (Time.millisToPosix 300) + + current = + Animator.move + (Timeline.atTime (Time.millisToPosix 300) currentTimeline) + toVals + + queued = + Animator.move + (Timeline.atTime (Time.millisToPosix 300) queuedTimeline) + toVals + in + Expect.within + (Absolute 0.001) + current + queued ] , test "Queue after some time" <| \_ -> @@ -266,8 +376,10 @@ queueing = Timeline.Timetable [ Timeline.Line (qty 0) (occur Starting (qty 0) (qty 4000)) - [ occur One (qty 5000) (qty 5000) - ] + [] + , Timeline.Line (qty 4000) + (occur One (qty 5000) (qty 5000)) + [] ] , initial = Starting , interruption = [] @@ -285,11 +397,11 @@ queueing = |> Animator.queue [ Animator.event (Animator.seconds 1) One ] - |> Timeline.updateNoGC (Time.millisToPosix 4000) + |> Timeline.updateWith False (Time.millisToPosix 4000) |> Animator.queue [ Animator.event (Animator.seconds 1) Two ] - |> Timeline.updateNoGC (Time.millisToPosix 7000) + |> Timeline.updateWith False (Time.millisToPosix 7000) in -- specifically we expect the dwell time on `Starting` and `One` to be extended Expect.equal @@ -299,9 +411,13 @@ queueing = Timeline.Timetable [ Timeline.Line (qty 0) (occur Starting (qty 0) (qty 4000)) - [ occur One (qty 5000) (qty 7000) - , occur Two (qty 8000) (qty 8000) - ] + [] + , Timeline.Line (qty 4000) + (occur One (qty 5000) (qty 7000)) + [] + , Timeline.Line (qty 7000) + (occur Two (qty 8000) (qty 8000)) + [] ] , initial = Starting , interruption = [] @@ -344,14 +460,14 @@ interruptions = , Animator.event (Animator.seconds 1) Unreachable , Animator.wait (Animator.seconds 1.0) ] - |> Timeline.updateNoGC (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 0) |> Animator.interrupt [ Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) Three , Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) Four ] - |> Timeline.updateNoGC (Time.millisToPosix 3000) + |> Timeline.updateWith False (Time.millisToPosix 3000) in describe "Interruptions" [ test "Correctly schedules" <| @@ -404,6 +520,66 @@ interruptions = in Expect.atLeast 0 (Animator.move new toVals) + , test "Long interruptions correctly schedule" <| + \_ -> + let + fourWithPause = + Animator.init Starting + |> Timeline.updateWith False (Time.millisToPosix 0) + |> Animator.queue + [ Animator.wait (Animator.seconds 1) + , Animator.event (Animator.seconds 1) One + , Animator.wait (Animator.seconds 1) + , Animator.event (Animator.seconds 1) Two + , Animator.wait (Animator.seconds 1) + , Animator.event (Animator.seconds 1) Three + , Animator.wait (Animator.seconds 1) + ] + |> Timeline.updateWith False (Time.millisToPosix 0) + |> Animator.interrupt + [ Animator.wait (Animator.seconds 1) + , Animator.event (Animator.seconds 1) One + , Animator.wait (Animator.seconds 1) + , Animator.event (Animator.seconds 1) Two + , Animator.wait (Animator.seconds 1) + , Animator.event (Animator.seconds 1) Three + , Animator.wait (Animator.seconds 1) + ] + |> Timeline.updateWith False (Time.millisToPosix 3000) + in + Expect.equal + fourWithPause + (Timeline.Timeline + { events = + Timeline.Timetable + [ Timeline.Line (qty 0) + (occur Starting (qty 0) (qty 1000)) + [] + , Timeline.Line (qty 1000) + (occur One (qty 2000) (qty 3000)) + [ occur Two (qty 4000) (qty 5000) + , occur Three (qty 6000) (qty 7000) + ] + , Timeline.Line (qty 4000) + (occur One + (qty 5000) + (qty 6000) + ) + [ occur Two + (qty 7000) + (qty 8000) + , occur Three + (qty 9000) + (qty 10000) + ] + ] + , initial = Starting + , interruption = [] + , now = qty 3000 + , queued = Nothing + , running = True + } + ) , test "Correctly schedules when an interruption already occurred" <| \_ -> let @@ -418,20 +594,20 @@ interruptions = , Animator.event (Animator.seconds 1) Unreachable , Animator.wait (Animator.seconds 1.0) ] - |> Timeline.updateNoGC (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 0) |> Animator.interrupt [ Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) Three , Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) Four ] - |> Timeline.updateNoGC (Time.millisToPosix 3000) + |> Timeline.updateWith False (Time.millisToPosix 3000) |> Animator.interrupt [ Animator.event (Animator.seconds 1) Two , Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) One ] - |> Timeline.updateNoGC (Time.millisToPosix 4500) + |> Timeline.updateWith False (Time.millisToPosix 4500) in Expect.equal doubleInterrupted @@ -480,7 +656,7 @@ interruptions = , Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) Three ] - |> Timeline.updateNoGC (Time.millisToPosix 0) + |> Timeline.updateWith False (Time.millisToPosix 0) interruptedAfterFinish = baseline @@ -489,7 +665,7 @@ interruptions = , Animator.wait (Animator.seconds 1.0) , Animator.event (Animator.seconds 1) Five ] - |> Timeline.updateNoGC (Time.millisToPosix 6000) + |> Timeline.updateWith False (Time.millisToPosix 6000) in Expect.equal interruptedAfterFinish @@ -847,6 +1023,31 @@ ordering = in Expect.true "Line order is not preserved" (Tuple.second order) + , test "Line order test case 3" <| + \_ -> + let + instructions = + Fuzz.Timeline.InstructionTimeline 0 + Two + [ -- Fuzz.Timeline.Interruption 0 [ ( 0, Four ), ( 0, Three ), ( 2, Four ), ( 0, Five ) ] + -- , Fuzz.Timeline.Interruption 1 [ ( 0, Four ), ( 0, Five ) ] + -- , + Fuzz.Timeline.Queue 0 [ ( 0, Four ), ( 0, Two ), ( 0, Four ) ] + ] + + actualTimeline = + Fuzz.Timeline.toTimeline { gc = False } instructions + in + case actualTimeline of + Timeline.Timeline details -> + case details.events of + Timeline.Timetable lines -> + let + order = + List.foldl isOrderPreserved ( 0, True ) lines + in + Expect.true "Line order is not preserved" + (Tuple.second order) , test "Line order test case 1" <| \_ -> let