Compare commits

...

6 Commits

Author SHA1 Message Date
Matthew Griffith
016ede0536 Remove remaining instances of velocity smoothing 2024-05-05 13:27:52 -04:00
Matthew Griffith
1bb5bf8b2c Remove velocity smoothing in transitionTo 2024-05-05 13:20:12 -04:00
Matthew Griffith
7531fc0d3f Discount durations based on the maximum progress made in any interruption 2024-05-05 11:19:31 -04:00
Matthew Griffith
4d150d82c1 Allow scaling of durations 2024-05-05 11:17:41 -04:00
Matthew Griffith
c45109cf96 Calculate Transitioning progress 2024-05-05 11:17:28 -04:00
Matthew Griffith
f9b5d0352e Fix initialization state 2024-05-04 17:26:30 -04:00
8 changed files with 246 additions and 93 deletions

View File

@ -294,7 +294,6 @@ keyframes steps =
Time.absolute (Time.millisToPosix 1)
firstEventTime =
-- Time.absolute (Time.millisToPosix 2)
imminent
( firstOccurring, remaining ) =

View File

@ -6,7 +6,7 @@ module Animator.Timeline exposing
, Step, wait, transitionTo
, scale, delay
, current, previous, upcoming, upcomingWith, arrived, arrivedAt, arrivedAtWith
, Duration
, Duration, hasChanges
)
{-|
@ -159,6 +159,16 @@ scale factor (Timeline.Timeline details) =
{ details | scale = min 5 (max 0.1 factor) }
{-| The proportion (number between 0 and 1) of progress between the last state and the new one.
Once we arrive at a new state, this value will be 1 until we start another transition.
-}
progress : Timeline state -> Float
progress =
Timeline.progress
{-| Get the current `state` of the timeline.
This value will switch to a new value when a transition begins.
@ -310,7 +320,7 @@ to duration ev timeline =
{-| Interrupt what's currently happening with a new list.
-}
interrupt : List (Step state) -> Timeline state -> Timeline state
interrupt steps (Timeline.Timeline tl) =
interrupt steps ((Timeline.Timeline tl) as fullTimeline) =
Timeline.Timeline
{ tl
| running = True
@ -323,12 +333,45 @@ interrupt steps (Timeline.Timeline tl) =
-- **NOTE** - if we recieve a new interruption, we throw away the existing one!
-- This was leading to issues when the same event was added to the `interrupted` queue
-- multiple times in before being scheduled.
-- So, I imagine it does make sense to dedup these
-- But does it ALWAYS make sense to replace the currently scheduled interruption?
[ List.foldl stepsToEvents schedule otherSteps ]
-- Also
-- If we're returning to a previous state while enroute to a new state,
-- we can "discount" the duration to return.
let
discountedSchedule =
if Duration.isZero (scheduleDelay schedule) && previous fullTimeline == currentScheduleTarget schedule then
let
transitionProgress =
Timeline.transitionProgress fullTimeline
in
schedule
|> scaleScheduleDurationBy (Maybe.withDefault 1 <| List.maximum transitionProgress)
else
schedule
in
[ List.foldl stepsToEvents discountedSchedule otherSteps ]
}
scaleScheduleDurationBy : Float -> Timeline.Schedule state -> Timeline.Schedule state
scaleScheduleDurationBy factor (Timeline.Schedule currentScheduleDelay (Timeline.Event dur checkpoint dwell) events) =
Timeline.Schedule
currentScheduleDelay
(Timeline.Event (Duration.scale factor dur) checkpoint dwell)
events
scheduleDelay : Timeline.Schedule state -> Time.Duration
scheduleDelay (Timeline.Schedule d _ _) =
d
currentScheduleTarget : Timeline.Schedule state -> state
currentScheduleTarget (Timeline.Schedule _ (Timeline.Event _ target _) _) =
target
{-| -}
initializeSchedule : Time.Duration -> List (Step state) -> Maybe ( Schedule state, List (Step state) )
initializeSchedule waiting steps =
case steps of
@ -413,3 +456,14 @@ update =
isRunning : Timeline state -> Bool
isRunning (Timeline.Timeline tl) =
tl.running
{-| -}
hasChanges : Timeline state -> Bool
hasChanges (Timeline.Timeline tl) =
case tl.queued of
Nothing ->
not (List.isEmpty tl.interruption)
Just _ ->
True

View File

@ -112,7 +112,7 @@ movement timeline lookup =
targetMovement =
lookup (Timeline.getEvent target)
in
Move.transitionTo progress
Move.at progress
startTransition
arrived
targetMovement

View File

@ -202,7 +202,7 @@ toInitialProps props rendered =
[] ->
rendered
(Prop id name _ format) :: remaining ->
(Prop id name movement format) :: remaining ->
toInitialProps remaining
(if Props.isTranslateId id then
case rendered.translation of
@ -214,14 +214,22 @@ toInitialProps props rendered =
, name = name
, format = format
, sections = []
, state = initVector 0
, state = Props.initVectorState id (Move.init movement)
}
, scale = rendered.scale
}
Just _ ->
Just translation ->
-- we've already initialized the transform
rendered
-- add the new prop to the list
{ props = rendered.props
, translation =
Just
{ translation
| state = Props.updateVectorById id (Move.init movement) translation.state
}
, scale = rendered.scale
}
else if Props.isScaleId id then
case rendered.scale of
@ -234,13 +242,20 @@ toInitialProps props rendered =
, name = name
, format = format
, sections = []
, state = initVector 1
, state = Props.initVectorState id (Move.init movement)
}
}
Just _ ->
Just scale ->
-- we've already initialized the transform
rendered
{ props = rendered.props
, scale =
Just
{ scale
| state = Props.updateVectorById id (Move.init movement) scale.state
}
, translation = rendered.translation
}
else
let
@ -684,7 +699,7 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
rendered.sections
, state =
rendered.state
|> Move.transitionTo progress
|> Move.at progress
startTime
targetTime
targetProp
@ -732,11 +747,6 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
getCommonTransformTransition
targetProps
Transition.standard
|> Transition.withVelocities fastestVelocity
-- If we do any transition smoothing
-- we'll need to normalize this velocity too
--Estimation.velocityAtTarget lookupState target future
0
else
getCommonTransformTransition

View File

@ -1,6 +1,6 @@
module InternalAnim.Css.Props exposing
( Id, ids, hash, default, defaultPosition, groups
, isTranslateId, isScaleId
, isTranslateId, isScaleId, initVector, initVectorState, updateVectorById
, Format, format, float, int, px, turns
, isGroup
, VectorSlot(..), colorHash, groupToCompoundId, noId, transparent, vectorSlotToId, vectorToString
@ -10,7 +10,7 @@ module InternalAnim.Css.Props exposing
@docs Id, ids, hash, default, defaultPosition, groups
@docs isTranslateId, isScaleId
@docs isTranslateId, isScaleId, initVector, initVectorState, updateVectorById
@docs Format, format, float, int, px, turns
@ -65,6 +65,105 @@ type alias Vector =
}
type alias VectorState =
{ x : Move.State
, y : Move.State
, z : Move.State
}
updateVectorById : Id -> val -> { x : val, y : val, z : val } -> { x : val, y : val, z : val }
updateVectorById id val vec =
case id of
0 ->
{ vec | x = val }
1 ->
{ vec | y = val }
2 ->
{ vec | z = val }
4 ->
{ x = val, y = val, z = val }
5 ->
{ vec | x = val }
6 ->
{ vec | y = val }
7 ->
{ vec | z = val }
_ ->
vec
initVectorState : Id -> Move.State -> VectorState
initVectorState id state =
case id of
0 ->
{ x = state, y = Move.toState 0, z = Move.toState 0 }
1 ->
{ x = Move.toState 0, y = state, z = Move.toState 0 }
2 ->
{ x = Move.toState 0, y = Move.toState 0, z = state }
4 ->
-- Scale all
{ x = state, y = state, z = state }
5 ->
-- scale x
{ x = state, y = Move.toState 1, z = Move.toState 1 }
6 ->
-- scale y
{ x = Move.toState 1, y = state, z = Move.toState 1 }
7 ->
-- scale z
{ x = Move.toState 1, y = Move.toState 1, z = state }
_ ->
{ x = Move.toState 0, y = Move.toState 0, z = Move.toState 0 }
initVector : Id -> Float -> Vector
initVector id val =
case id of
0 ->
{ x = val, y = 0, z = 0 }
1 ->
{ x = 0, y = val, z = 0 }
2 ->
{ x = 0, y = 0, z = val }
4 ->
-- Scale all
{ x = val, y = val, z = val }
5 ->
-- scale x
{ x = val, y = 1, z = 1 }
6 ->
-- scale y
{ x = 1, y = val, z = 1 }
7 ->
-- scale z
{ x = 1, y = 1, z = val }
_ ->
{ x = 0, y = 0, z = 0 }
{-| Giving the scaling group, return the property that sets all scaling/
-}
groupToCompoundId : Id -> Maybe Id

View File

@ -1,4 +1,4 @@
module InternalAnim.Duration exposing (Duration, inMilliseconds, inSeconds, isZero, milliseconds)
module InternalAnim.Duration exposing (Duration, inMilliseconds, inSeconds, isZero, milliseconds, scale)
import InternalAnim.Quantity as Quantity
@ -11,6 +11,11 @@ type Seconds
= Seconds
scale : Float -> Duration -> Duration
scale factor (Quantity.Quantity seconds) =
Quantity.Quantity (factor * seconds)
isZero : Duration -> Bool
isZero (Quantity.Quantity seconds) =
seconds == 0

View File

@ -7,15 +7,15 @@ module InternalAnim.Move exposing
, sequences
, addSequence, cssForSections
, withTransition, withVelocities
, at, transitionTo
, move
, at
, move, toState
)
{-|
@docs Move, to, toWith
@docs State, init
@docs State, init, fromFloat
@docs lerpColor, lerpFloat, lerpVector
@ -28,7 +28,7 @@ module InternalAnim.Move exposing
@docs withTransition, withVelocities
@docs at, transitionTo
@docs at
-}
@ -56,6 +56,14 @@ init movement =
}
toState : Float -> State
toState x =
{ position =
Units.pixels x
, velocity = Units.pixelsPerSecond 0
}
{-|
Adjust the transition by taking into account intro and exit velocity if necessary
@ -231,66 +239,6 @@ at progress startTime targetTime (Pos transition targetPosition dwell) startingS
targetPosition
{-| This is the same as `at`, but with velocity transitions built in.
-}
transitionTo :
Float
-> Time.Absolute
-> Time.Absolute
-> Move Float
-> State
-> State
transitionTo progress startTime targetTime (Pos trans targetPosition dwell) startingState =
let
startPosition =
Units.inPixels startingState.position
introVelocity =
normalizeVelocity
startTime
targetTime
startPosition
targetPosition
startingState.velocity
-- exitVelocity =
-- If we do any transition smoothing
-- we'll need to normalize this velocity too
--Estimation.velocityAtTarget lookupState target future
transition =
if introVelocity == 0 then
trans
else
Transition.withVelocities introVelocity 0 trans
in
Transition.atX progress transition
|> denormalize startTime
targetTime
startPosition
targetPosition
normalizeVelocity :
Time.Absolute
-> Time.Absolute
-> Float
-> Float
-> Units.PixelsPerSecond
-> Float
normalizeVelocity startTime targetTime startPosition targetPosition velocity =
let
pixelsPerSecond =
Units.inPixelsPerSecond velocity
in
if pixelsPerSecond == 0 then
0
else
(pixelsPerSecond * Duration.inSeconds (Time.duration startTime targetTime))
/ (targetPosition - startPosition)
{-|
Go from 0-1:0-1

View File

@ -10,6 +10,7 @@ module InternalAnim.Timeline exposing
, foldpAll
, gc, atTime, dwellingTime, getCurrentTime, linesAreActive
, Transition
, transitionProgress
)
{-|
@ -78,6 +79,8 @@ type alias TimelineDetails event =
, events : Timetable event
, queued : Maybe (Schedule event)
, interruption : List (Schedule event)
-- Running means that there are ongoing events
, running : Bool
}
@ -1088,19 +1091,44 @@ visitAll2 toAnchor transitionTo details prev queue future state =
type Status
= Dwelling Time.Duration
| Transitioning Float
| Transitioning
{ progress : Float
, transitionProgress : List Float
}
status : Timeline event -> Status
status timeline =
foldpAll identity
(\_ -> Dwelling Time.zeroDuration)
(\_ _ _ now start end _ _ ->
if Time.thisAfterThat now end then
Dwelling (Time.duration now end)
(\_ prev target now start end theFuture found ->
-- Some notes because I have this loaded in my brain now.
-- end: either the endtime of `target event` or the interruption time
-- We generally care about progress towards the start time of the target
-- so we don't want to use `end` necessarily.
let
startTimeTarget =
startTime target
in
if Time.thisAfterThat now startTimeTarget then
Dwelling (Time.duration now startTimeTarget)
else
Transitioning (Time.progress start end now)
case found of
Transitioning trans ->
Transitioning
{ progress =
Time.progress start startTimeTarget now
, transitionProgress =
trans.progress :: trans.transitionProgress
}
Dwelling _ ->
Transitioning
{ progress =
Time.progress start startTimeTarget now
, transitionProgress = []
}
)
timeline
@ -1118,7 +1146,17 @@ progress timeline =
1
Transitioning t ->
t
t.progress
transitionProgress : Timeline state -> List Float
transitionProgress timeline =
case status timeline of
Dwelling _ ->
[]
Transitioning t ->
t.progress :: t.transitionProgress
{-| The number of milliseconds that has occurred since we came to rest at the most recent state.