A bunch of elm-review cleanup

This commit is contained in:
Matthew Griffith 2024-05-04 08:00:01 -04:00
parent 65b635e9ef
commit 963d415161
14 changed files with 318 additions and 2180 deletions

View File

@ -118,16 +118,6 @@ opacity o =
InternalAnim.Css.Props.float
{-| -}
xAsSingleProp : Float -> Attribute
xAsSingleProp o =
Css.Prop
InternalAnim.Css.Props.ids.opacity
"transform"
(Move.to o)
InternalAnim.Css.Props.translateX
{-| -}
scale : Float -> Attribute
scale s =
@ -189,6 +179,7 @@ rotationAround axis n =
(InternalAnim.Css.Props.turns axis)
zAxis : { x : Float, y : Float, z : Float }
zAxis =
{ x = 0, y = 0, z = 1 }
@ -261,21 +252,6 @@ ms =
Duration.milliseconds
{-| When transitioning to this state, start with a little extra velocity!
This takes a number from 0-1.
-}
withImpulse : Float -> Attribute -> Attribute
withImpulse impulse prop =
case prop of
Css.Prop id name move format ->
Css.Prop id name (Move.withVelocities impulse 0 move) format
Css.ColorProp name move ->
Css.ColorProp name (Move.withVelocities impulse 0 move)
{-| -}
delay : Duration -> Animation -> Animation
delay dur (Animation now attrs) =
@ -340,7 +316,7 @@ keyframes steps =
[] ->
[]
(Step dur props) :: _ ->
(Step _ props) :: _ ->
props
, now = imminent
, delay = Time.zeroDuration
@ -508,12 +484,12 @@ formatColorSteps steps prop pastSteps =
Nothing ->
List.reverse pastSteps
Just (Css.Prop id name _ format) ->
Just (Css.Prop _ _ _ _) ->
formatColorSteps next
prop
pastSteps
Just (Css.ColorProp name (Move.Pos trans value _)) ->
Just (Css.ColorProp _ (Move.Pos trans value _)) ->
formatColorSteps next
prop
(Move.stepWith dur trans value :: pastSteps)
@ -534,12 +510,12 @@ formatSteps steps prop pastSteps =
Nothing ->
List.reverse pastSteps
Just (Css.Prop id name (Move.Pos trans value _) format) ->
Just (Css.Prop _ _ (Move.Pos trans value _) _) ->
formatSteps next
prop
(Move.stepWith dur trans value :: pastSteps)
Just (Css.ColorProp name movement) ->
Just (Css.ColorProp _ _) ->
formatSteps next
prop
pastSteps
@ -683,7 +659,7 @@ pinging dur =
]
{-| Animate an element on a specific timeline. Check out [`Animator.Timeline`](#Animator/Timeline) for more details.
{-| Animate an element on a specific timeline. Check out [`Animator.Timeline`](https://package.elm-lang.org/packages/mdgriffith/elm-animator/latest/Animator-Timeline) for more details.
This will

View File

@ -6,6 +6,7 @@ module Animator.Timeline exposing
, Step, wait, transitionTo
, scale, delay
, current, previous, upcoming, upcomingWith, arrived, arrivedAt, arrivedAtWith
, Duration
)
{-|

View File

@ -93,11 +93,8 @@ movement : Timeline state -> (state -> Movement) -> { position : Float, velocity
movement timeline lookup =
Timeline.foldpAll lookup
Move.init
(\_ prev target now startTransition interruptedOrEnd future state ->
(\_ _ target now startTransition interruptedOrEnd future state ->
let
arrived =
Timeline.startTime target
isHappening =
Time.thisAfterOrEqualThat now startTransition
|| (List.isEmpty future
@ -106,6 +103,9 @@ movement timeline lookup =
in
if isHappening then
let
arrived =
Timeline.startTime target
progress =
Time.progress startTransition arrived now

View File

@ -1,4 +1,4 @@
module InternalAnim.Bits exposing (Bits, has, init, off, on, store4, store4Float, value, zeroes)
module InternalAnim.Bits exposing (Bits, store4Float, value)
{-| Let's make storing values within a single Int a bit easier to do while not compromising performance.
@ -24,14 +24,6 @@ type Bits bits
= Bits Int
type Size
= Flag
type Offset
= Offset
-- flip : Int -> Bool -> Bits bits -> Bits bits
-- flip offset on (Bits b) =
@ -40,43 +32,6 @@ type Offset
-- |> Bits
init : Bits bits
init =
Bits zeroes
has : Int -> Bits bits -> Bool
has offset (Bits b) =
let
target =
zeroes |> store offset 1 ones
in
Bitwise.and target b - target == 0
on : Int -> Bits bits -> Bits bits
on offset (Bits b) =
b
|> store offset 1 ones
|> Bits
off : Int -> Bits bits -> Bits bits
off offset (Bits b) =
b
|> store offset 1 zeroes
|> Bits
bool : Bool -> Int
bool yes =
if yes then
ones
else
zeroes
store4Float : Float -> Float -> Float -> Float -> Bits bits
store4Float one two three four =
Bitwise.and top8 (round one)
@ -89,28 +44,6 @@ store4Float one two three four =
|> Bits
store4 : Int -> Int -> Int -> Int -> Bits bits
store4 one two three four =
Bitwise.and top8 one
|> Bitwise.or
(Bitwise.shiftLeftBy 8 (Bitwise.and top8 two))
|> Bitwise.or
(Bitwise.shiftLeftBy 16 (Bitwise.and top8 three))
|> Bitwise.or
(Bitwise.shiftLeftBy 24 (Bitwise.and top8 four))
|> Bits
store : Int -> Int -> Int -> Int -> Int
store offset length val target =
target
get : Int -> Int -> Int -> Int
get offset length val =
val
{-| -}
ones : Int
ones =
@ -124,21 +57,6 @@ zeroes =
Bitwise.or 0 0
top10 : Int
top10 =
Bitwise.shiftRightZfBy (32 - 10) ones
top8 : Int
top8 =
Bitwise.shiftRightZfBy (32 - 8) ones
top6 : Int
top6 =
Bitwise.shiftRightZfBy (32 - 6) ones
top5 : Int
top5 =
Bitwise.shiftRightZfBy (32 - 5) ones

View File

@ -1,7 +1,6 @@
module InternalAnim.Css exposing
( Prop(..)
, RenderedProp(..)
, cssFromProps
, match
, propsToRenderedProps
, toCss
@ -9,21 +8,15 @@ module InternalAnim.Css exposing
{-| -}
import Bezier
import Color
import Html
import Html.Attributes as Attr exposing (id)
import InternalAnim.Bits as Bits
import InternalAnim.Css.Props as Props
import InternalAnim.Duration as Duration
import InternalAnim.Hash as Hash
import InternalAnim.Move as Move
import InternalAnim.Quantity as Quantity
import InternalAnim.Time as Time
import InternalAnim.Timeline as Timeline
import InternalAnim.Transition as Transition
import InternalAnim.Units as Units
import Set exposing (Set)
{-| An id representing a prop type.
@ -68,16 +61,6 @@ type Prop
| ColorProp String (Move.Move Color.Color)
isTransformProp : Prop -> Bool
isTransformProp prop =
case prop of
Prop id name _ _ ->
Props.isTransformId id
ColorProp _ _ ->
False
isGroupProp : Id -> Prop -> Bool
isGroupProp groupId prop =
case prop of
@ -162,21 +145,6 @@ toCss now renderedProps =
}
cssFromProps : Timeline.Timeline state -> (state -> List Prop) -> CssAnim
cssFromProps timeline lookup =
let
present =
getInitial timeline lookup
renderedProps =
Timeline.foldpAll lookup
(\_ -> present)
toPropCurves2
timeline
in
props2Css (Timeline.getCurrentTime timeline) renderedProps emptyAnim
getInitial : Timeline.Timeline event -> (event -> List Prop) -> List RenderedProp
getInitial timeline lookup =
let
@ -185,7 +153,7 @@ getInitial timeline lookup =
(\props ->
toInitialProps props { props = [], translation = Nothing, scale = Nothing }
)
(\get prev target now startTime endTime future cursor ->
(\get _ target _ _ _ _ cursor ->
addInitialProps (get (Timeline.getEvent target)) cursor
)
timeline
@ -205,6 +173,7 @@ addMaybeVector maybeVector renderedProps =
VectorProp vector :: renderedProps
initState : Float -> Move.State
initState x =
{ position =
Units.pixels x
@ -233,12 +202,7 @@ toInitialProps props rendered =
[] ->
rendered
(Prop id name movement format) :: remaining ->
let
state =
Move.init
(Props.default id)
in
(Prop id name _ format) :: remaining ->
toInitialProps remaining
(if Props.isTranslateId id then
case rendered.translation of
@ -279,6 +243,11 @@ toInitialProps props rendered =
rendered
else
let
state =
Move.init
(Props.default id)
in
{ props =
RenderedProp
{ id = id
@ -313,29 +282,23 @@ matchProp id renderedProp =
RenderedProp details ->
details.id - id == 0
RenderedColorProp details ->
RenderedColorProp _ ->
False
TransformProp details ->
False
VectorProp details ->
VectorProp _ ->
False
matchColor : String -> RenderedProp -> Bool
matchColor name renderedProp =
case renderedProp of
RenderedProp details ->
RenderedProp _ ->
False
RenderedColorProp details ->
details.name == name
TransformProp details ->
False
VectorProp details ->
VectorProp _ ->
False
@ -347,7 +310,7 @@ addInitialProps props rendered =
[] ->
rendered
(Prop id name movement format) :: remaining ->
(Prop id name _ format) :: remaining ->
let
new =
if Props.isTranslateId id then
@ -537,36 +500,6 @@ props2Css now renderedProps anim =
|> combine anim
)
(TransformProp details) :: remain ->
props2Css now
remain
(case details.sections of
[] ->
{ anim
| hash = transformToHash (stateToTransform details.state) ++ anim.hash
, props =
( "transform"
, renderTransformState details.state
)
:: anim.props
}
_ ->
Move.cssForSections now
(stateToTransform details.state)
"transform"
(\t one two ->
"transform: "
++ transformToString
(Move.lerpTransform t one two)
)
transformToString
transformToHash
(List.reverse details.sections)
emptyAnim
|> combine anim
)
vectorToHash : Props.Id -> Vector -> String
vectorToHash group vec =
@ -594,38 +527,6 @@ vectorToHash group vec =
++ Hash.float vec.z
transformToHash : { a | x : Float, y : Float, rotation : Float, scale : Float } -> String
transformToHash trans =
let
scaleStr =
if trans.scale == 1 then
""
else
"s" ++ String.fromInt (round (trans.scale * 100))
rotationStr =
if trans.rotation == 0 then
""
else
"r" ++ String.fromInt (round (trans.rotation * 100))
translateStr =
if trans.x == 0 && trans.y == 0 then
""
else
"t"
++ String.fromInt (round trans.x)
++ "-"
++ String.fromInt (round trans.y)
in
translateStr
++ rotationStr
++ scaleStr
vectorStateToVector : VectorState -> Vector
vectorStateToVector state =
{ x =
@ -637,99 +538,6 @@ vectorStateToVector state =
}
stateToTransform : TransformState -> Transform
stateToTransform state =
{ x =
Units.inPixels state.x.position
, y =
Units.inPixels state.y.position
, scale =
Units.inPixels state.scale.position
, rotation =
Units.inPixels state.rotation.position
}
renderTransformState :
{ a
| x : { b | position : Units.Pixels }
, y : { c | position : Units.Pixels }
, rotation : { d | position : Units.Pixels }
, scale : { e | position : Units.Pixels }
}
-> String
renderTransformState state =
"translate("
++ pixelsToString state.x.position
++ "px, "
++ pixelsToString state.y.position
++ "px) rotate("
++ pixelsToString state.rotation.position
++ "turn)"
++ " scale("
++ pixelsToString state.scale.position
++ ")"
pixelsToString : Units.Pixels -> String
pixelsToString pixels =
floatToString (Units.inPixels pixels)
floatToString : Float -> String
floatToString plusMinus =
let
float =
abs plusMinus
base =
floor float
decimal =
floor (100 * (float - toFloat base))
toString f =
if plusMinus < 0 then
"-" ++ String.fromInt f
else
String.fromInt f
in
if decimal == 0 then
toString base
else
toString base ++ "." ++ String.fromInt decimal
transformToString trans =
"translate("
++ floatToString trans.x
++ "px, "
++ floatToString trans.y
++ "px) rotate("
++ floatToString trans.rotation
++ "turn)"
++ " scale("
++ floatToString trans.scale
++ ")"
infinite : String
infinite =
"infinite"
isEmptyAnim : { css | keyframes : String } -> Bool
isEmptyAnim anim =
case anim.keyframes of
"" ->
True
_ ->
False
emptyAnim : CssAnim
emptyAnim =
{ hash = ""
@ -824,37 +632,6 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
|> lookup
|> stateOrDefault rendered.id
rendered.name
finalProp =
-- this is the check for being a transition
if not (Time.equal (Timeline.endTime prev) startTime) then
-- adjust the transition by taking into account
-- the intro and exit velocity
-- but only if this is an interruption
targetProp
|> Move.withVelocities
(normalizeVelocity
startTime
targetTime
startPosition
targetPosition
rendered.state.velocity
)
-- If we do any transition smoothing
-- we'll need to normalize this velocity too
--Estimation.velocityAtTarget lookupState target future
0
else
targetProp
startPosition =
Units.inPixels rendered.state.position
targetPosition =
case targetProp of
Move.Pos _ x _ ->
x
in
RenderedProp
{ id = rendered.id
@ -865,6 +642,39 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
rendered.sections
else
let
finalProp =
-- this is the check for being a transition
if not (Time.equal (Timeline.endTime prev) startTime) then
-- adjust the transition by taking into account
-- the intro and exit velocity
-- but only if this is an interruption
let
startPosition =
Units.inPixels rendered.state.position
targetPosition =
case targetProp of
Move.Pos _ x _ ->
x
in
targetProp
|> Move.withVelocities
(normalizeVelocity
startTime
targetTime
startPosition
targetPosition
rendered.state.velocity
)
-- If we do any transition smoothing
-- we'll need to normalize this velocity too
--Estimation.velocityAtTarget lookupState target future
0
else
targetProp
in
Move.sequences
startTime
targetTime
@ -933,11 +743,6 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
targetProps
Transition.standard
commonSequence =
getCommonVectorSequence details.group
targetProps
[]
targets =
{ x =
getVectorSlot details.group Props.X targetProps
@ -946,11 +751,6 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
, z =
getVectorSlot details.group Props.Z targetProps
}
commonMovement =
Move.move commonTransition
targets
commonSequence
in
VectorProp
{ group = details.group
@ -961,6 +761,17 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
details.sections
else
let
commonSequence =
getCommonVectorSequence details.group
targetProps
[]
commonMovement =
Move.move commonTransition
targets
commonSequence
in
Move.sequences
startTime
targetTime
@ -995,139 +806,6 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
details.state.z
}
}
TransformProp details ->
-- for each prop
-- calculate a new state
-- calculate a new transition
-- (for now), take the most "different" curve
-- Compose a new `Move Transform` with the transition
--
let
targetProps : List Prop
targetProps =
Timeline.getEvent target
|> lookup
|> List.filter isTransformProp
commonTransition =
if not (Time.equal (Timeline.endTime prev) startTime) then
let
fastestVelocity =
firstNonZero
[ normalizeVelocity
startTime
targetTime
(Units.inPixels details.state.x.position)
targets.x
details.state.x.velocity
, normalizeVelocity
startTime
targetTime
(Units.inPixels details.state.y.position)
targets.y
details.state.y.velocity
, normalizeVelocity
startTime
targetTime
(Units.inPixels details.state.rotation.position)
targets.rotation
details.state.rotation.velocity
, normalizeVelocity
startTime
targetTime
(Units.inPixels details.state.scale.position)
targets.scale
details.state.scale.velocity
]
in
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
targetProps
Transition.standard
commonSequence =
getCommonTransformSequence
targetProps
[]
targets =
{ x =
transformOrDefault Props.ids.x
targetProps
, y =
transformOrDefault Props.ids.y
targetProps
, scale =
transformOrDefault Props.ids.scale
targetProps
, rotation =
transformOrDefault Props.ids.rotation
targetProps
}
commonMovement =
Move.move commonTransition
targets
commonSequence
in
TransformProp
{ sections =
if finished then
details.sections
else
Move.sequences
startTime
targetTime
now
endTime
commonMovement
details.sections
, state =
{ x =
Move.at progress
startTime
targetTime
(Move.toWith commonTransition
targets.x
)
details.state.x
, y =
Move.at progress
startTime
targetTime
(Move.toWith commonTransition
targets.y
)
details.state.y
, scale =
Move.at progress
startTime
targetTime
(Move.toWith commonTransition
targets.scale
)
details.state.scale
, rotation =
Move.at progress
startTime
targetTime
(Move.toWith commonTransition
targets.rotation
)
details.state.rotation
}
}
)
cursor
@ -1158,7 +836,7 @@ getCommonVectorSequence :
-> List (Move.Sequence Vector)
getCommonVectorSequence groupId props sequences =
case props of
(Prop id name (Move.Pos trans v propSeq) format) :: _ ->
(Prop _ _ (Move.Pos _ _ propSeq) _) :: _ ->
-- sequences
vectorSeq groupId props propSeq 0 []
@ -1210,7 +888,7 @@ gatherVectorSteps groupId seqLevel stepLevel steps props transforms =
[] ->
transforms
(Move.Step dur trans target) :: remainingSteps ->
(Move.Step dur trans _) :: remainingSteps ->
gatherVectorSteps groupId
seqLevel
(stepLevel + 1)
@ -1221,6 +899,7 @@ gatherVectorSteps groupId seqLevel stepLevel steps props transforms =
)
getVectorStepAt : Id -> Duration.Duration -> Transition.Transition -> Int -> Int -> List Prop -> Move.Step Vector
getVectorStepAt groupId dur trans seqLevel stepLevel props =
let
x =
@ -1262,109 +941,6 @@ getVectorStepAt groupId dur trans seqLevel stepLevel props =
{- END VECTOR -}
{-|
*warning! this need to be called with pre-filtered props that are only transform props!
-}
getCommonTransformSequence :
List Prop
-> List (Move.Sequence Transform)
-> List (Move.Sequence Transform)
getCommonTransformSequence props sequences =
case props of
(Prop id name (Move.Pos trans v propSeq) format) :: _ ->
-- sequences
transformSeq props propSeq 0 []
_ ->
sequences
transformSeq :
List Prop
-> List (Move.Sequence Float)
-> Int
-> List (Move.Sequence Transform)
-> List (Move.Sequence Transform)
transformSeq props pilotSequence seqLevel renderedTransforms =
case pilotSequence of
[] ->
renderedTransforms
(Move.Sequence n delay dur steps) :: remain ->
transformSeq props
remain
(seqLevel + 1)
(Move.Sequence n
delay
dur
(gatherSequenceSteps seqLevel
0
steps
props
[]
)
:: renderedTransforms
)
gatherSequenceSteps :
Int
-> Int
-> List (Move.Step Float)
-> List Prop
-> List (Move.Step Transform)
-> List (Move.Step Transform)
gatherSequenceSteps seqLevel stepLevel steps props transforms =
case steps of
[] ->
transforms
(Move.Step dur trans target) :: remainingSteps ->
gatherSequenceSteps seqLevel
(stepLevel + 1)
remainingSteps
props
(getTransformStepAt dur trans seqLevel stepLevel props
:: transforms
)
getTransformStepAt dur trans seqLevel stepLevel props =
Move.Step dur
trans
{ x =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.x
props
Nothing
, y =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.y
props
Nothing
, scale =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.scale
props
Nothing
, rotation =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.rotation
props
Nothing
}
getTransformSequenceValueAt : Int -> Int -> Maybe Props.Id -> Props.Id -> List Prop -> Maybe Float -> Float
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId props defaultValue =
case props of
@ -1376,7 +952,7 @@ getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId props def
Just default ->
default
(Prop id name move _) :: remain ->
(Prop id _ move _) :: remain ->
if id - targetId == 0 then
case move of
Move.Pos _ v seq ->
@ -1420,7 +996,7 @@ getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId props def
else
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId remain defaultValue
(ColorProp name movement) :: remain ->
(ColorProp _ _) :: remain ->
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId remain defaultValue
@ -1447,7 +1023,7 @@ getCommonTransformTransition props currentTrans =
[] ->
currentTrans
(Prop id _ (Move.Pos trans _ _) _) :: remain ->
(Prop _ _ (Move.Pos trans _ _) _) :: remain ->
if Transition.isStandard trans then
getCommonTransformTransition remain currentTrans
@ -1478,7 +1054,7 @@ valueOrDefault maybeDefaultid targetId props defaultVal =
Just val ->
val
(Prop id name move _) :: remain ->
(Prop id _ move _) :: remain ->
if id - targetId == 0 then
case move of
Move.Pos _ v _ ->
@ -1498,30 +1074,10 @@ valueOrDefault maybeDefaultid targetId props defaultVal =
else
valueOrDefault maybeDefaultid targetId remain defaultVal
(ColorProp name movement) :: remain ->
(ColorProp _ _) :: remain ->
valueOrDefault maybeDefaultid targetId remain defaultVal
{-| -}
transformOrDefault : Id -> List Prop -> Float
transformOrDefault targetId props =
case props of
[] ->
Props.defaultPosition targetId
(Prop id name move _) :: remain ->
if id - targetId == 0 then
case move of
Move.Pos _ v _ ->
v
else
transformOrDefault targetId remain
(ColorProp name movement) :: remain ->
transformOrDefault targetId remain
{-| -}
stateOrDefault : Id -> String -> List Prop -> Move.Move Float
stateOrDefault targetId targetName props =
@ -1543,7 +1099,7 @@ stateOrDefault targetId targetName props =
else
stateOrDefault targetId targetName remain
(ColorProp name movement) :: remain ->
(ColorProp _ _) :: remain ->
stateOrDefault targetId targetName remain
@ -1554,7 +1110,7 @@ colorOrDefault targetName default props =
[] ->
default
(Prop id _ move _) :: remain ->
(Prop _ _ _ _) :: remain ->
colorOrDefault targetName default remain
(ColorProp name (Move.Pos _ clr _)) :: remain ->
@ -1565,30 +1121,6 @@ colorOrDefault targetName default props =
colorOrDefault targetName default remain
matchForMovement : Id -> String -> List Prop -> Maybe (Move.Move Float)
matchForMovement onlyId onlyName props =
case props of
[] ->
Nothing
(ColorProp name move) :: remain ->
matchForMovement onlyId onlyName remain
((Prop id name movement _) as top) :: remain ->
if id + 1 == 0 then
if name == onlyName then
Just movement
else
matchForMovement onlyId onlyName remain
else if id - onlyId == 0 then
Just movement
else
matchForMovement onlyId onlyName remain
{-| A group of curves represents the trail of one scalar property
(Scalar property meaning something like opacity, or just the `R` channel of rgb.)
@ -1597,7 +1129,6 @@ matchForMovement onlyId onlyName props =
type RenderedProp
= RenderedProp RenderedPropDetails
| RenderedColorProp RenderedColorPropDetails
| TransformProp TransformPropDetails
-- transform can now be deconstructed into its parts
-- This is for translation and scaling
-- Rotation is a RenderedProp
@ -1645,33 +1176,6 @@ type alias RenderedColorPropDetails =
}
type alias TransformPropDetails =
{ sections :
List (Move.Sequence Transform)
, state : TransformState
}
type alias TransformState =
{ x : Move.State
, y : Move.State
, scale : Move.State
, rotation : Move.State
}
type alias Transform =
{ x : Float
, y : Float
, scale : Float
, rotation : Float
}
type alias TransformPresence =
Bits.Bits Transform
{-| Slightly different than CssAnim in that we can also have style properties
This is for when a property has not changed and so a full animation is not necessary
-}

View File

@ -1,22 +1,19 @@
module InternalAnim.Css.Props exposing
( Id, ids, hash, default, defaultPosition, groups
, isTransformId, isTranslateId, isRotateId, isScaleId
, isTranslateId, isScaleId
, Format, format, float, int, px, turns
, roundFloat, floatToString
, isGroup
, VectorSlot(..), colorHash, groupToCompoundId, name, noId, toStr, translateX, transparent, vectorSlotToId, vectorToString, zero
, VectorSlot(..), colorHash, groupToCompoundId, noId, transparent, vectorSlotToId, vectorToString
)
{-|
@docs Id, ids, hash, default, defaultPosition, groups
@docs isTransformId, isTranslateId, isRotateId, isScaleId
@docs isTranslateId, isScaleId
@docs Format, format, float, int, px, turns
@docs roundFloat, floatToString
@docs translateToString, isGroup
-}
@ -139,12 +136,9 @@ hashFormat form num =
Px ->
String.fromInt (round num) ++ "px"
Turns vec ->
Turns _ ->
String.fromInt (round num)
TranslateX ->
"translateX(" ++ String.fromInt (round num) ++ "px)"
format : Format -> Float -> String
format form num =
@ -162,15 +156,11 @@ format form num =
-- Number here is 1/1000 of a turn
vectorToCssString vec ++ " " ++ String.fromFloat (num / 1000) ++ "turn"
TranslateX ->
"translateX(" ++ String.fromInt (round num) ++ "px)"
type Format
= AsFloat
| AsInt
| Px
| TranslateX
| Turns Vector
@ -194,11 +184,6 @@ px =
Px
translateX : Format
translateX =
TranslateX
{-| We make this huge because we want it last.
The order of the ids matters, as it's the order that they're rendered in.
@ -249,12 +234,24 @@ isGroup groupId id =
False
groups : { scaling : Id, translation : Id }
groups =
{ scaling = 20
, translation = 10
}
ids :
{ x : Id
, y : Id
, z : Id
, rotation : Id
, scale : Id
, scaleX : Id
, scaleY : Id
, scaleZ : Id
, opacity : Id
}
ids =
{ x = 0
, y = 1
@ -268,30 +265,15 @@ ids =
}
firstTransform : Id
firstTransform =
ids.x
type alias Id =
Int
isTransformId : Id -> Bool
isTransformId id =
id < 12
isTranslateId : Id -> Bool
isTranslateId id =
id < 3
isRotateId : Id -> Bool
isRotateId id =
id == 3
isScaleId : Id -> Bool
isScaleId id =
id == 4 || id == 5 || id == 6 || id == 7
@ -382,60 +364,6 @@ hashId id =
"unknown"
name : Id -> String
name id =
case id of
13 ->
"opacity"
14 ->
"background-color"
_ ->
"unknown"
toStr : Id -> (Float -> String)
toStr id =
case id of
0 ->
\f ->
"translateX(" ++ String.fromFloat f ++ "px)"
1 ->
\f ->
"translateY(" ++ String.fromFloat f ++ "px)"
2 ->
\f ->
"translateZ(" ++ String.fromFloat f ++ "px)"
3 ->
\f ->
"rotate(" ++ String.fromFloat f ++ "rad)"
4 ->
\f ->
"scale(" ++ String.fromFloat f ++ ")"
5 ->
\f ->
"scaleX(" ++ String.fromFloat f ++ ")"
6 ->
\f ->
"scaleY(" ++ String.fromFloat f ++ ")"
13 ->
-- opacity
\f ->
String.fromFloat f
_ ->
\f ->
String.fromFloat f
defaultPosition : Id -> Float
defaultPosition id =
case id of

View File

@ -1,130 +0,0 @@
module InternalAnim.Estimation exposing (velocity, velocityAtTarget)
{-| -}
import Animator
import Animator.Timeline
import Animator.Value
import InternalAnim.Duration as Duration
import InternalAnim.Move as Move
import InternalAnim.Quantity as Quantity
import InternalAnim.Time as Time
import InternalAnim.Timeline as Timeline
import InternalAnim.Units as Units exposing (Pixels, PixelsPerSecond)
import Time
mapTime fn time =
Time.millisToPosix (fn (Time.posixToMillis time))
{-| Estimate velocity in pixels/second
-}
velocity : Int -> Time.Posix -> Animator.Timeline.Timeline event -> (event -> Animator.Value.Movement) -> Float
velocity resolution time timeline toPosition =
let
before =
mapTime (\t -> t - resolution) time
after =
mapTime (\t -> t + resolution) time
zero =
Animator.Value.movement (Timeline.atTime before timeline) toPosition
one =
Animator.Value.movement (Timeline.atTime time timeline) toPosition
two =
Animator.Value.movement (Timeline.atTime after timeline) toPosition
first =
(one.position - zero.position) / toFloat resolution
second =
(two.position - one.position) / toFloat resolution
expected =
-- 1000 * avg first second
1000 * (two.position - zero.position) / (2 * toFloat resolution)
in
expected
{-| -}
velocityAtTarget :
(state -> Move.Move Float)
-> Timeline.Occurring state
-> List (Timeline.Occurring state)
-> PixelsPerSecond
velocityAtTarget lookup target future =
let
movement =
lookup (Timeline.getEvent target)
in
case future of
[] ->
case movement of
Move.Pos _ _ [] ->
zeroVelocity
Move.Pos _ _ (seq :: _) ->
Move.initialSequenceVelocity seq
next :: _ ->
let
targetPosition =
case movement of
Move.Pos _ x _ ->
Units.pixels x
in
case lookup (Timeline.getEvent next) of
Move.Pos _ aheadPosition [] ->
-- our target velocity is the linear velocity between target and lookahead
velocityBetween
targetPosition
(Timeline.endTime target)
(Units.pixels aheadPosition)
(Timeline.startTime next)
Move.Pos _ aheadPosition (seq :: _) ->
if Timeline.isResting target then
Move.initialSequenceVelocity seq
else
velocityBetween
targetPosition
(Timeline.endTime target)
(Units.pixels aheadPosition)
(Timeline.startTime next)
velocityBetween :
Pixels
-> Time.Absolute
-> Pixels
-> Time.Absolute
-> PixelsPerSecond
velocityBetween one oneTime two twoTime =
let
distance =
two
|> Quantity.minus one
duration =
Time.duration oneTime twoTime
vel =
Units.inPixels distance
/ Duration.inSeconds duration
in
if isNaN vel || isInfinite vel then
Quantity.zero
else
Units.pixelsPerSecond vel
zeroVelocity : PixelsPerSecond
zeroVelocity =
Units.pixelsPerSecond 0

View File

@ -1,15 +1,14 @@
module InternalAnim.Move exposing
( Move(..), to, toWith
, State, init
, lerpColor, lerpFloat, lerpTransform, lerpVector
, lerpColor, lerpFloat, lerpVector
, Sequence(..)
, Step(..), step, stepWith, set
, Step(..), stepWith
, sequences
, addSequence, cssForSections
, withTransition, withVelocities
, at, atX, transitionTo
, denormalize, normalizeOver, toReal
, floatToString, initialSequenceVelocity, move
, at, transitionTo
, move
)
{-|
@ -18,10 +17,10 @@ module InternalAnim.Move exposing
@docs State, init
@docs lerpColor, lerpFloat, lerpTransform, lerpVector
@docs lerpColor, lerpFloat, lerpVector
@docs Sequence
@docs Step, step, stepWith, set
@docs Step, stepWith
@docs sequences, goto
@ -29,9 +28,7 @@ module InternalAnim.Move exposing
@docs withTransition, withVelocities
@docs at, atX, transitionTo
@docs denormalize, normalizeOver, toReal
@docs at, transitionTo
-}
@ -121,6 +118,7 @@ toWith t v =
Pos t v []
move : value -> Move value
move =
Pos
@ -140,27 +138,12 @@ withSequenceDelay delay (Sequence i _ dur steps) =
Sequence i delay dur steps
set : value -> Step value
set =
Step zeroDuration Transition.standard
step : Duration.Duration -> value -> Step value
step dur value =
Step dur Transition.standard value
stepWith : Duration.Duration -> Transition.Transition -> value -> Step value
stepWith =
Step
{--}
zeroVelocity : Units.PixelsPerSecond
zeroVelocity =
Units.pixelsPerSecond 0
type alias State =
{ position : Units.Pixels
, velocity : Units.PixelsPerSecond
@ -185,14 +168,6 @@ addDelayToSequence delay seqs captured =
)
type alias Transform =
{ x : Float
, y : Float
, scale : Float
, rotation : Float
}
lerpFloat : Float -> Float -> Float -> Float
lerpFloat t one two =
one + ((two - one) * t)
@ -237,30 +212,6 @@ lerpVector t one two =
}
lerpTransform : Float -> Transform -> Transform -> Transform
lerpTransform t one two =
{ x =
lerpFloat t one.x two.x
, y =
lerpFloat t one.y two.y
, scale =
lerpFloat t one.scale two.scale
, rotation =
lerpFloat t one.rotation two.rotation
}
atX :
Float
-> Move Float
->
{ position : Bezier.Point
, velocity : Bezier.Point
}
atX progress (Pos trans value dwell) =
Transition.atX2 progress trans
at :
Float
-> Time.Absolute
@ -273,7 +224,7 @@ at progress startTime targetTime (Pos transition targetPosition dwell) startingS
startPosition =
Units.inPixels startingState.position
in
Transition.atX2 progress transition
Transition.atX progress transition
|> denormalize startTime
targetTime
startPosition
@ -313,7 +264,7 @@ transitionTo progress startTime targetTime (Pos trans targetPosition dwell) star
else
Transition.withVelocities introVelocity 0 trans
in
Transition.atX2 progress transition
Transition.atX progress transition
|> denormalize startTime
targetTime
startPosition
@ -389,26 +340,6 @@ scaleXYBy { x, y } point =
{ x = point.x * x, y = point.y * y }
{-|
Map a value to 0:1 given a range it should be in.
-}
normalizeOver : Float -> Float -> Float -> Float
normalizeOver start end current =
let
total =
abs (end - start)
in
if total == 0 then
0
else
((current - start) / total)
|> max 0
|> min 1
{-| The opposite of `normalizeOver`.
I guess this is denormalization? Though i was always confused by that term :/
@ -481,162 +412,160 @@ sequences :
-> List (Sequence value)
-> List (Sequence value)
sequences startTime targetTime now stopTime movement existingSequence =
let
durationToNow =
Time.duration startTime now
in
if Time.equal now stopTime && not (Time.equal targetTime stopTime) then
-- We've probably been interrupted
[]
else if Time.thisAfterOrEqualThat startTime now then
-- We've definitely started, so we want to report the full sequence
-- most common case will be startTime == now
case movement of
Pos trans value [] ->
let
transitionDuration =
Time.duration startTime targetTime
seq =
Sequence 1
durationToNow
transitionDuration
[ Step transitionDuration trans value
]
in
push seq existingSequence
Pos trans value [ Sequence 1 delay dur steps ] ->
let
stepDuration =
Time.duration startTime targetTime
transitionDuration =
stepDuration
|> Time.expand dur
transitionSequence =
Sequence 1
(Time.expand durationToNow delay)
transitionDuration
(Step stepDuration trans value
:: steps
)
in
push transitionSequence
existingSequence
Pos trans value dwell ->
let
transitionDuration =
Time.duration startTime targetTime
transitionSequence =
Sequence 1
durationToNow
transitionDuration
[ Step transitionDuration trans value
]
in
if Time.isZeroDuration transitionDuration && not (List.isEmpty dwell) then
existingSequence
|> append (addDelayToSequence durationToNow dwell [])
else
existingSequence
|> push transitionSequence
|> append (addDelayToSequence (Time.expand durationToNow transitionDuration) dwell [])
else if after startTime stopTime now movement then
-- we've completely passed this state, no splines are returned
[]
else
-- now is during the new sequence
-- so let's compose the new sequence and then split it at the new time
-- we also know that existingSequence should be [] here
case movement of
Pos trans value [] ->
let
splitTime =
Time.progress startTime targetTime now
transitionDuration =
Time.duration startTime targetTime
newSequence =
Sequence 1 Quantity.zero transitionDuration [ Step transitionDuration trans value ]
|> takeAfter durationToNow
in
case newSequence.following of
Nothing ->
[ newSequence.base ]
Just following ->
[ newSequence.base ]
|> push
(following
|> withSequenceDelay (getSequenceDuration newSequence.base)
)
Pos trans value [ Sequence 1 delay dur steps ] ->
let
stepDuration =
Time.duration startTime targetTime
transitionDuration =
stepDuration
|> Time.expand dur
new =
Sequence 1 Quantity.zero transitionDuration (Step stepDuration trans value :: steps)
|> takeAfter durationToNow
in
case new.following of
Nothing ->
[ new.base
]
Just following ->
[ new.base ]
|> push
(following |> withSequenceDelay (getSequenceDuration new.base))
Pos trans value dwell ->
let
transitionDuration =
Time.duration startTime targetTime
in
if Time.thisAfterThat now targetTime then
takeAfterSequenceList durationToNow dwell
else
let
durationToNow =
Time.duration startTime now
in
if Time.thisAfterOrEqualThat startTime now then
-- We've definitely started, so we want to report the full sequence
-- most common case will be startTime == now
case movement of
Pos trans value [] ->
let
new =
transitionDuration =
Time.duration startTime targetTime
seq =
Sequence 1
durationToNow
transitionDuration
[ Step transitionDuration trans value
]
in
push seq existingSequence
Pos trans value [ Sequence 1 delay dur steps ] ->
let
stepDuration =
Time.duration startTime targetTime
transitionDuration =
stepDuration
|> Time.expand dur
transitionSequence =
Sequence 1
(Time.expand durationToNow delay)
transitionDuration
(Step stepDuration trans value
:: steps
)
in
push transitionSequence
existingSequence
Pos trans value dwell ->
let
transitionDuration =
Time.duration startTime targetTime
in
if Time.isZeroDuration transitionDuration && not (List.isEmpty dwell) then
existingSequence
|> append (addDelayToSequence durationToNow dwell [])
else
let
transitionSequence =
Sequence 1
durationToNow
transitionDuration
[ Step transitionDuration trans value
]
in
existingSequence
|> push transitionSequence
|> append (addDelayToSequence (Time.expand durationToNow transitionDuration) dwell [])
else if after startTime stopTime now movement then
-- we've completely passed this state, no splines are returned
[]
else
-- now is during the new sequence
-- so let's compose the new sequence and then split it at the new time
-- we also know that existingSequence should be [] here
case movement of
Pos trans value [] ->
let
transitionDuration =
Time.duration startTime targetTime
newSequence =
Sequence 1 Quantity.zero transitionDuration [ Step transitionDuration trans value ]
|> takeAfter durationToNow
in
case newSequence.following of
Nothing ->
[ newSequence.base ]
Just following ->
[ newSequence.base ]
|> push
(following
|> withSequenceDelay (getSequenceDuration newSequence.base)
)
Pos trans value [ Sequence 1 _ dur steps ] ->
let
stepDuration =
Time.duration startTime targetTime
transitionDuration =
stepDuration
|> Time.expand dur
new =
Sequence 1 Quantity.zero transitionDuration (Step stepDuration trans value :: steps)
|> takeAfter durationToNow
in
case new.following of
Nothing ->
new.base
:: addDelayToSequence (getSequenceDuration new.base) dwell []
[ new.base
]
Just following ->
let
delayToFollowing =
getSequenceDuration new.base
in
[ new.base ]
|> push (following |> withSequenceDelay delayToFollowing)
|> append
(addDelayToSequence
(Time.expand (getSequenceDuration following)
delayToFollowing
|> push
(following |> withSequenceDelay (getSequenceDuration new.base))
Pos trans value dwell ->
if Time.thisAfterThat now targetTime then
takeAfterSequenceList durationToNow dwell
else
let
transitionDuration =
Time.duration startTime targetTime
new =
Sequence 1 Quantity.zero transitionDuration [ Step transitionDuration trans value ]
|> takeAfter durationToNow
in
case new.following of
Nothing ->
new.base
:: addDelayToSequence (getSequenceDuration new.base) dwell []
Just following ->
let
delayToFollowing =
getSequenceDuration new.base
in
[ new.base ]
|> push (following |> withSequenceDelay delayToFollowing)
|> append
(addDelayToSequence
(Time.expand (getSequenceDuration following)
delayToFollowing
)
dwell
[]
)
dwell
[]
)
takeAfterSequenceList :
@ -648,7 +577,7 @@ takeAfterSequenceList durationToNow seqs =
[] ->
[]
((Sequence n delay duration steps) as top) :: remain ->
((Sequence n _ duration _) as top) :: remain ->
let
floatN =
toFloat n
@ -824,7 +753,7 @@ afterSequenceList durationTillNow seq remaining =
[] ->
True
((Sequence n delay duration steps) as top) :: rest ->
((Sequence n _ duration _) as top) :: rest ->
let
durationOfUnrolledSeq =
duration |> Quantity.multiplyBy (toFloat n)
@ -838,6 +767,7 @@ afterSequenceList durationTillNow seq remaining =
False
afterSequence : Duration.Duration -> Sequence value -> Bool
afterSequence durationTillNow (Sequence n delay duration steps) =
let
floatN =
@ -863,21 +793,6 @@ zeroDuration =
{- CSS KEYFRAMES -}
initialSequenceVelocity : Sequence value -> Units.PixelsPerSecond
initialSequenceVelocity seq =
case seq of
Sequence 0 _ _ _ ->
zeroVelocity
Sequence _ _ _ [] ->
zeroVelocity
Sequence n _ _ ((Step dur trans _) :: _) ->
Transition.initialVelocity trans
|> (*) 1000
|> Units.pixelsPerSecond
hash : Time.Absolute -> String -> Sequence value -> (value -> String) -> String
hash now name (Sequence n delay dur steps) toString =
-- we need to encode the current time in the animations name so the browser doesn't cache anything
@ -914,16 +829,6 @@ stepHash steps toString hashed =
)
roundFloat : Float -> Float
roundFloat f =
toFloat (round (f * 100)) / 100
floatToString : Float -> String
floatToString f =
String.fromFloat (roundFloat f)
hashDuration : String -> Duration.Duration -> String
hashDuration prefix dur =
let
@ -939,38 +844,6 @@ hashDuration prefix dur =
(round (Duration.inSeconds dur))
lastPosOr : value -> Sequence value -> value
lastPosOr x (Sequence _ _ _ steps) =
case steps of
[] ->
x
_ ->
lastPosOrHelper x steps
lastPosOrHelper : value -> List (Step value) -> value
lastPosOrHelper x steps =
case steps of
[] ->
x
(Step _ _ v) :: [] ->
v
(Step _ _ _) :: (Step _ _ v) :: [] ->
v
(Step _ _ _) :: (Step _ _ _) :: (Step _ _ v) :: [] ->
v
(Step _ _ _) :: (Step _ _ _) :: (Step _ _ _) :: (Step _ _ v) :: [] ->
v
(Step _ _ _) :: (Step _ _ _) :: (Step _ _ _) :: (Step _ _ v) :: remain ->
lastPosOrHelper v remain
type alias CssAnim =
{ hash : String
, animation : String
@ -1005,7 +878,7 @@ cssForSections now startPos name lerp toString toHashString sections anim =
[] ->
anim
[ (Sequence 1 delay dur [ Step stepDur (Transition.Transition spline) v ]) as seq ] ->
[ (Sequence 1 delay _ [ Step stepDur (Transition.Transition spline) v ]) as seq ] ->
-- NOTE, we're not using the above `dur` because it's the total duration of the sequence
-- and therefore equal to stepDur in this case
{ anim
@ -1156,7 +1029,7 @@ initialProps name toString startPos (Sequence _ _ _ steps) =
[] ->
[]
(Step dur transition val) :: _ ->
(Step dur _ val) :: _ ->
if Duration.isZero dur then
[ ( name, toString val ) ]
@ -1219,16 +1092,6 @@ keyframeHelper name lerp startPos sequenceDuration currentDur steps rendered =
(rendered ++ frames)
emptyAnim : CssAnim
emptyAnim =
{ hash = ""
, animation = ""
, transition = ""
, keyframes = ""
, props = []
}
combine : CssAnim -> CssAnim -> CssAnim
combine one two =
if String.isEmpty one.hash && List.isEmpty one.props then

View File

@ -4,12 +4,7 @@ module InternalAnim.Quantity exposing
, equalWithin
, greaterThan
, greaterThanOrEqualTo
, greaterThanOrEqualToZero
, greaterThanZero
, lessThan
, lessThanOrEqualTo
, lessThanOrEqualToZero
, lessThanZero
, max
, minus
, multiplyBy
@ -38,12 +33,6 @@ minus (Quantity y) (Quantity x) =
Quantity (x - y)
{-| -}
lessThan : Quantity number units -> Quantity number units -> Bool
lessThan (Quantity y) (Quantity x) =
x < y
{-| -}
greaterThan : Quantity number units -> Quantity number units -> Bool
greaterThan (Quantity y) (Quantity x) =
@ -62,30 +51,6 @@ greaterThanOrEqualTo (Quantity y) (Quantity x) =
x >= y
{-| -}
lessThanZero : Quantity number units -> Bool
lessThanZero (Quantity x) =
x < 0
{-| -}
greaterThanZero : Quantity number units -> Bool
greaterThanZero (Quantity x) =
x > 0
{-| -}
lessThanOrEqualToZero : Quantity number units -> Bool
lessThanOrEqualToZero (Quantity x) =
x <= 0
{-| -}
greaterThanOrEqualToZero : Quantity number units -> Bool
greaterThanOrEqualToZero (Quantity x) =
x >= 0
max : Quantity number units -> Quantity number units -> Quantity number units
max (Quantity x) (Quantity y) =
Quantity (Basics.max x y)

View File

@ -3,6 +3,7 @@ module InternalAnim.Random exposing (random)
{-| -}
fract : Float -> Float
fract x =
x - toFloat (floor x)
@ -20,4 +21,4 @@ random seed low high =
0
else
(fract (sin seed * 100000.0) + 1.5707) / pi
low + (((fract (sin seed * 100000.0) + 1.5707) / pi) * (high - low))

View File

@ -2,8 +2,8 @@ module InternalAnim.Time exposing
( thisBeforeOrEqualThat, thisAfterOrEqualThat, equal
, Absolute, AbsoluteTime(..), Duration, absolute, duration, progress
, inMilliseconds
, latest, earliest, toPosix, durationToString, reduceDurationBy
, advanceBy, equalDuration, expand, isZeroDuration, maxDuration, millis, numberOfFrames, positiveDuration, progressWithin, rollbackBy, scaleDuration, thisAfterThat, thisBeforeThat, zeroDuration
, latest, toPosix, durationToString
, advanceBy, expand, isZeroDuration, maxDuration, millis, positiveDuration, progressWithin, rollbackBy, scaleDuration, thisAfterThat, thisBeforeThat, zeroDuration
)
{-|
@ -14,7 +14,7 @@ module InternalAnim.Time exposing
@docs inMilliseconds
@docs latest, earliest, toPosix, durationToString, reduceDurationBy
@docs latest, toPosix, durationToString
-}
@ -74,11 +74,6 @@ expand one two =
Quantity.plus one two
reduceDurationBy : Duration -> Duration -> Duration
reduceDurationBy one two =
Quantity.minus one two
advanceBy : Duration -> Absolute -> Absolute
advanceBy dur time =
Quantity.plus time (Quantity.Quantity (Duration.inMilliseconds dur))
@ -147,15 +142,6 @@ latest ((Quantity.Quantity one) as oneQty) ((Quantity.Quantity two) as twoQty) =
oneQty
earliest : Absolute -> Absolute -> Absolute
earliest ((Quantity.Quantity one) as oneQty) ((Quantity.Quantity two) as twoQty) =
if (one - two) >= 0 then
twoQty
else
oneQty
thisBeforeThat : Absolute -> Absolute -> Bool
thisBeforeThat (Quantity.Quantity this) (Quantity.Quantity that) =
(this - that) < 0
@ -189,34 +175,3 @@ isZeroDuration (Quantity.Quantity dur) =
equal : Absolute -> Absolute -> Bool
equal (Quantity.Quantity this) (Quantity.Quantity that) =
(this - that) == 0
equalDuration : Duration -> Duration -> Bool
equalDuration (Quantity.Quantity this) (Quantity.Quantity that) =
(this - that) == 0
{-| The number of frames, and the offset that's needed to preserve the framerate.
Offset
-}
numberOfFrames : Float -> Absolute -> Absolute -> Absolute -> ( Float, Int )
numberOfFrames fps lastFrameTime startAt endAt =
let
millisecondsPerFrame =
1000 / fps
totalDurationInMs =
Duration.inMilliseconds (duration startAt endAt)
framesSinceLastFrame =
max 0 (Duration.inMilliseconds (duration lastFrameTime startAt))
/ millisecondsPerFrame
offset =
1 - (framesSinceLastFrame - toFloat (floor framesSinceLastFrame))
in
( offset * millisecondsPerFrame
, max 1 (round (totalDurationInMs / millisecondsPerFrame))
)

View File

@ -1,35 +1,40 @@
module InternalAnim.Timeline exposing
( Timeline(..), TimelineDetails, Occurring(..), getEvents
( Timeline(..), TimelineDetails, Occurring(..)
, Schedule(..), Event(..)
, needsUpdate, update, updateWith
, startTime, endTime, getEvent, extendEventDwell, hasDwell, isResting
, update, updateWith
, startTime, endTime, getEvent, extendEventDwell
, addToDwell
, sendPing
, current, arrivedAt, arrived, previous, upcoming
, progress
, Line(..), Timetable(..)
, foldpAll, captureTimeline
, ActualDuration(..), Animator(..), Description(..), Frame(..), Frames(..), FramesSummary, Interp, LookAhead, Period(..), Previous(..), Resting(..), Summary, SummaryEvent(..), Transition, atTime, combineRunning, dwellingTime, gc, getCurrentTime, hasChanged, justInitialized, linesAreActive, periodDuration, progress
, foldpAll
, gc, atTime, dwellingTime, getCurrentTime, linesAreActive
, Transition
)
{-|
@docs Timeline, TimelineDetails, Occurring, getEvents
@docs Timeline, TimelineDetails, Occurring
@docs Schedule, Event
@docs needsUpdate, update, updateWith
@docs update, updateWith
@docs startTime, endTime, getEvent, extendEventDwell, hasDwell, isResting
@docs startTime, endTime, getEvent, extendEventDwell
@docs addToDwell
@docs sendPing
@docs current, arrivedAt, arrived, previous, upcoming
@docs progress
@docs Line, Timetable
@docs foldpAll, captureTimeline
@docs foldpAll
@docs gc, atTime, dwellingTime, getCurrentTime, linesAreActive
@docs Transition
-}
@ -50,21 +55,6 @@ type Event event
= Event Time.Duration event (Maybe Time.Duration)
type Period
= Loop Time.Duration
| Repeat Int Time.Duration
periodDuration : Period -> Time.Duration
periodDuration per =
case per of
Loop dur ->
dur
Repeat i dur ->
dur
getScheduledEvent : Event event -> event
getScheduledEvent (Event _ ev _) =
ev
@ -121,29 +111,6 @@ type Occurring event
{- TYPES FOR INTERPOLATION -}
{-| First, let's cover what the type parameters are
Examples:
The symbolic state of the timeline entry.
state -> MenuOpen
The description of how to animate that symbolic state
anchor ->
= Osc Personality Period (Float -> Float)
| Pos Personality Float
The actual value that's moving
motion -> { x:34, y: 34 }
-}
type alias Interp state anchor motion =
{ start : anchor -> motion
, visit : Visit state anchor motion
, transition : Lerp anchor motion
}
type alias Transition state anchor motion =
(state -> anchor)
-- previous event
@ -167,74 +134,6 @@ type alias Transition state anchor motion =
-> motion
type alias Visit state anchor motion =
(state -> anchor)
-> Occurring state
-> Time.Absolute
-> Maybe (LookAhead anchor)
-> motion
-> motion
type alias LookAhead state =
{ anchor : state
, time : Time.Absolute
, resting : Bool
}
mapLookAhead : (a -> b) -> LookAhead a -> LookAhead b
mapLookAhead fn look =
{ anchor = fn look.anchor
, time = look.time
, resting = look.resting
}
type alias Lerp anchor motion =
Time.Absolute
-> anchor
-> anchor
-> Time.Absolute
-> Time.Absolute
-> Maybe (LookAhead anchor)
-> motion
-> motion
type Previous event
= Previous (Occurring event)
| PreviouslyInterrupted Time.Absolute
mapTable : (Occurring a -> Occurring b) -> Timetable a -> Timetable b
mapTable fn (Timetable lines) =
Timetable (List.map (mapLine fn) lines)
mapLine : (Occurring a -> Occurring b) -> Line a -> Line b
mapLine fn (Line t startEvent els) =
Line t (fn startEvent) (List.map fn els)
mapLineWith : (Occurring a -> state -> ( Occurring b, state )) -> state -> Line a -> ( Line b, state )
mapLineWith fn initial (Line start startingEvent remaining) =
let
onLine occur ( events, state ) =
let
( newOccur, newState ) =
fn occur state
in
( newOccur :: events, newState )
( newStartingEvent, startingState ) =
fn startingEvent initial
in
case List.foldl onLine ( [], startingState ) remaining of
( reversedEvents, newState ) ->
( Line start newStartingEvent (List.reverse reversedEvents), newState )
getEvent : Occurring event -> event
getEvent (Occurring ev _ _) =
ev
@ -249,16 +148,6 @@ extendEventDwell extendBy ((Event at ev maybeDwell) as thisEvent) =
Event at ev (addToDwell extendBy maybeDwell)
hasDwell : Occurring event -> Bool
hasDwell (Occurring _ (Quantity.Quantity start) (Quantity.Quantity end)) =
(start - end) /= 0
isResting : Occurring event -> Bool
isResting (Occurring _ (Quantity.Quantity start) (Quantity.Quantity end)) =
(end - start) == 0
startTime : Occurring event -> Time.Absolute
startTime (Occurring _ time _) =
time
@ -269,26 +158,6 @@ endTime (Occurring _ _ end) =
end
type Description event
= DescribeStartTransition Time.Posix
| DescribeEvent Time.Posix event
| DescribeInterruption
{ interruption : Time.Posix
, target : event
, newTarget : event
, newTargetTime : Time.Posix
}
getEvents : Timeline event -> List (List ( Time.Posix, event ))
getEvents (Timeline timeline) =
case timeline.events of
Timetable lines ->
lines
|> List.map (\(Line _ start ev) -> start :: ev)
|> List.map (List.map (\(Occurring evt time _) -> ( Time.toPosix time, evt )))
atTime : Time.Posix -> Timeline event -> Timeline event
atTime now (Timeline timeline) =
Timeline { timeline | now = Time.absolute now }
@ -334,11 +203,6 @@ updateWith withGC possiblyNow (Timeline timeline) =
clean : Bool -> TimelineDetails event -> TimelineDetails event
clean runGC details =
let
events =
case details.events of
Timetable evs ->
evs
running =
case details.events of
Timetable lines ->
@ -349,6 +213,12 @@ clean runGC details =
running
, events =
if runGC then
let
events =
case details.events of
Timetable evs ->
evs
in
Timetable (garbageCollectOldEvents details.now [] events)
else
@ -597,7 +467,7 @@ scaleEvent scale (Event dur event maybeDur) =
-}
scheduleMatchesExisting : Schedule event -> Line event -> Bool
scheduleMatchesExisting (Schedule scheduleDelay event schedulUpcoming) (Line lineStart lineStartEvent lineUpcoming) =
scheduleMatchesExisting (Schedule _ event schedulUpcoming) (Line _ lineStartEvent lineUpcoming) =
let
equalStartEvent =
scheduledEventEqual event lineStartEvent
@ -619,7 +489,7 @@ scheduleMatchesExisting (Schedule scheduleDelay event schedulUpcoming) (Line lin
scheduledEventEqual : Event event -> Occurring event -> Bool
scheduledEventEqual (Event dur schedEvent maybeDwell) (Occurring occurEvent occurStart occurEnd) =
scheduledEventEqual (Event _ schedEvent _) (Occurring occurEvent _ _) =
schedEvent == occurEvent
@ -685,17 +555,17 @@ interrupt timeline scheduled =
interruptLines : Time.Absolute -> Schedule event -> List (Line event) -> List (Line event) -> Maybe (List (Line event))
interruptLines now scheduled pastLines lines =
let
startInterruption =
case scheduled of
Schedule scheduleDelay _ _ ->
Time.advanceBy scheduleDelay now
in
case lines of
[] ->
Nothing
startLine :: remaining ->
let
startInterruption =
case scheduled of
Schedule scheduleDelay _ _ ->
Time.advanceBy scheduleDelay now
in
if interruptionHappensLater startInterruption remaining then
interruptLines now scheduled (startLine :: pastLines) remaining
@ -731,14 +601,14 @@ interruptionHappensLater startInterruption remaining =
interruptLine : Time.Absolute -> Schedule event -> Line event -> List (Line event) -> Maybe (List (Line event))
interruptLine now scheduled line future =
let
startInterruption =
case scheduled of
Schedule scheduleDelay _ _ ->
Time.advanceBy scheduleDelay now
in
case line of
Line start startEvent trailing ->
let
startInterruption =
case scheduled of
Schedule scheduleDelay _ _ ->
Time.advanceBy scheduleDelay now
in
if Time.thisAfterOrEqualThat startInterruption start then
-- this line starts before the interruption
case future of
@ -803,18 +673,19 @@ getTransitionAt interruptionTime prev trailing =
interruptAtExactly : Time.Absolute -> Schedule event -> LastTwoEvents event -> Line event
interruptAtExactly now scheduled ((LastTwoEvents penultimateTime penultimate lastEventTime lastEvent) as last) =
interruptAtExactly now scheduled (LastTwoEvents penultimateTime penultimate lastEventTime _) =
case scheduled of
Schedule delay_ startingEvent reverseQueued ->
let
amountProgress =
Time.progress penultimateTime
lastEventTime
(Time.advanceBy delay_ now)
newStartingEvent =
-- we apply the discount if we are returning to a state
if penultimate == getScheduledEvent startingEvent then
let
amountProgress =
Time.progress penultimateTime
lastEventTime
(Time.advanceBy delay_ now)
in
startingEvent
|> adjustScheduledDuration (Quantity.multiplyBy amountProgress)
@ -1008,33 +879,6 @@ addToDwell duration maybeDwell =
Just (Quantity.plus duration existing)
rescale : Time.Absolute -> Float -> List (Line event) -> List (Line event)
rescale now scale lines =
if scale == 1 then
lines
else
List.map (rescaleLine now scale) lines
rescaleLine : Time.Absolute -> Float -> Line event -> Line event
rescaleLine now scale (Line lineStart firstEvent remain) =
Line
(rescaleTime now scale lineStart)
(rescaleEvent now scale firstEvent)
(List.map (rescaleEvent now scale) remain)
rescaleTime : Time.Absolute -> Float -> Time.Absolute -> Time.Absolute
rescaleTime (Quantity.Quantity now) scale (Quantity.Quantity time) =
Time.millis (now + ((time - now) * scale))
rescaleEvent : Time.Absolute -> Float -> Occurring event -> Occurring event
rescaleEvent now scale (Occurring event start end) =
Occurring event (rescaleTime now scale start) (rescaleTime now scale end)
foldpAll :
(state -> anchor)
-> (anchor -> motion)
@ -1052,7 +896,7 @@ foldpAll lookup toStart transitionTo (Timeline timelineDetails) =
[] ->
start
(Line lineStart firstEvent remain) :: _ ->
(Line lineStart _ _) :: _ ->
visitAll2
lookup
transitionTo
@ -1238,176 +1082,6 @@ visitAll2 toAnchor transitionTo details prev queue future state =
new
type alias FramesSummary motion =
{ frames : List (Frame motion)
, duration : Time.Duration
, dwell :
Maybe
{ period : Period
, frames : List (Frame motion)
}
}
type Frame motion
= Frame Float motion
type alias Summary event =
{ events : List (SummaryEvent event)
, now : Time.Absolute
, startTime : Time.Absolute
}
type SummaryEvent event
= EventSummary event Time.Absolute ActualDuration
| InterruptionSummary
{ target : event
, targetTime : Time.Absolute
, interruptedAt : Time.Absolute
, newTarget : event
, newTargetTime : Time.Absolute
, newTargetDuration : ActualDuration
}
type ActualDuration
= OpenDuration
| KnownDuration Time.Duration
{-| -}
type Frames item
= Single item
| Hold Int item
| Walk item (List (Frames item))
| WithRest (Resting item) (Frames item)
{-| -}
type Resting item
= Cycle Period (List (Frames item))
captureTimeline :
(state -> anchor)
-> Timeline state
-> Summary anchor
captureTimeline lookup (Timeline timelineDetails) =
case timelineDetails.events of
Timetable timetable ->
case timetable of
[] ->
-- I believe this case is fleeting because as soon as we have a real time,
-- we add a line to the timetable.
-- However, maybe it is awkwardly rendered once?
{ events =
[ EventSummary
(lookup timelineDetails.initial)
timelineDetails.now
OpenDuration
]
, now = timelineDetails.now
, startTime = timelineDetails.now
}
(Line start startEv remain) :: remainingLines ->
let
events =
captureTimelineHelper lookup
(startEv :: remain)
remainingLines
[]
in
{ events = List.reverse events
, now = timelineDetails.now
, startTime = start
}
{-| Summarize all the events on the current timeline.
Note, this does not take into account time adjustments!
Essentially this is only used for sprite animation, which currently dont have leaveLate or arriveEarly.
-}
captureTimelineHelper :
(state -> anchor)
-> List (Occurring state)
-> List (Line state)
-> List (SummaryEvent anchor)
-> List (SummaryEvent anchor)
captureTimelineHelper lookup events futureLines summary =
-- futureStart starts a new line.
-- if an interruption occurs, we want to interpolate to the point of the interruption
-- then transition over to the new line.
case events of
[] ->
case futureLines of
[] ->
summary
(Line futureStart futureStartEv futureRemain) :: restOfFuture ->
captureTimelineHelper lookup (futureStartEv :: futureRemain) restOfFuture summary
(Occurring event start eventEnd) :: remain ->
case futureLines of
[] ->
case remain of
[] ->
let
newEvent =
EventSummary (lookup event) start OpenDuration
in
newEvent :: summary
_ ->
let
newEvent =
EventSummary (lookup event) start (KnownDuration (Time.duration start eventEnd))
in
captureTimelineHelper lookup remain futureLines (newEvent :: summary)
(Line futureStart futureStartEv futureRemain) :: restOfFuture ->
if Time.thisBeforeOrEqualThat futureStart start then
-- interruption
let
newEvent =
InterruptionSummary
{ target = lookup event
, targetTime = start
, interruptedAt = futureStart
, newTarget = lookup (getEvent futureStartEv)
, newTargetTime = startTime futureStartEv
, newTargetDuration =
case futureRemain of
[] ->
case restOfFuture of
[] ->
OpenDuration
_ ->
KnownDuration (Time.duration start eventEnd)
_ ->
KnownDuration (Time.duration start eventEnd)
}
in
captureTimelineHelper lookup futureRemain restOfFuture (newEvent :: summary)
else
-- queue up new events
let
newEvent =
EventSummary (lookup event)
start
(KnownDuration (Time.duration start eventEnd))
in
captureTimelineHelper lookup remain futureLines (newEvent :: summary)
{- BOOKKEEPING -}
@ -1418,10 +1092,10 @@ type Status
status : Timeline event -> Status
status ((Timeline details) as timeline) =
status timeline =
foldpAll identity
(\_ -> Dwelling Time.zeroDuration)
(\lookup prev target now start end future state ->
(\_ _ _ now start end _ _ ->
if Time.thisAfterThat now end then
Dwelling (Time.duration now end)
@ -1466,7 +1140,7 @@ arrived : Timeline event -> event
arrived ((Timeline details) as timeline) =
foldpAll identity
(\_ -> details.initial)
(\lookup prev target now start end future state ->
(\_ _ target now _ end _ state ->
-- This is the current event when
-- we have started toward an event or arrived at it.
-- A tricky aspect is that css timelines are only updated on transition
@ -1484,7 +1158,7 @@ current : Timeline event -> event
current ((Timeline details) as timeline) =
foldpAll identity
(\_ -> details.initial)
(\lookup prev target now start end future state ->
(\_ _ target now start end future state ->
-- This is the current event when
-- we have started toward an event or arrived at it.
-- A tricky aspect is that css timelines are only updated on transition
@ -1508,7 +1182,7 @@ previous : Timeline event -> event
previous ((Timeline details) as timeline) =
foldpAll identity
(\_ -> details.initial)
(\lookup prev target now start end future state ->
(\_ _ target now _ _ future state ->
if Time.thisAfterThat now (endTime target) then
case future of
[] ->
@ -1527,7 +1201,7 @@ arrivedAt : (event -> Bool) -> Time.Posix -> Timeline event -> Bool
arrivedAt matches newTime ((Timeline details) as tl) =
foldpAll identity
(\_ -> False)
(\lookup prev target now start end future state ->
(\_ _ target _ _ end _ state ->
state
|| (matches (getEvent target)
&& Time.thisBeforeOrEqualThat details.now end
@ -1553,7 +1227,7 @@ matchesEvent matches (Event _ event _) =
anyScheduled : (event -> Bool) -> Schedule event -> Bool
anyScheduled matches (Schedule dur startEvent remainingEvents) =
anyScheduled matches (Schedule _ startEvent remainingEvents) =
if matchesEvent matches startEvent then
True
@ -1576,149 +1250,10 @@ upcoming matches ((Timeline details) as tl) =
else
foldpAll identity
(\_ -> False)
(\lookup prev target now start end future state ->
(\_ _ target now _ end _ state ->
state
|| (matches (getEvent target)
&& Time.thisBeforeThat now end
)
)
tl
{- ANIMATOR -}
{- The animator checks to see if any timelines are running and also has the ability to update the animation state.
Different animators can do different things
- Normal -> always on
- Inline -> on when moving (require anotation of dwelling events)
- CSS -> single update when timeline is updated
-}
{-| -}
type Animator model
= Animator (model -> Running) (Time.Posix -> model -> model)
type alias Running =
{ running : Bool
, ping : Maybe { delay : Float, target : Time.Posix }
}
combineRunning : Running -> Running -> Running
combineRunning one two =
{ running = one.running || two.running
, ping =
case two.ping of
Nothing ->
one.ping
Just twoPing ->
case one.ping of
Nothing ->
Just twoPing
Just onePing ->
if onePing.delay < twoPing.delay then
Just onePing
else
Just twoPing
}
sendPing : Timeline event -> Maybe { delay : Float, target : Time.Posix }
sendPing ((Timeline details) as timeline) =
Maybe.andThen (encodeStamp details.now) (findNextTransitionTime timeline)
findNextTransitionTime : Timeline event -> Maybe Time.Absolute
findNextTransitionTime ((Timeline details) as timeline) =
foldpAll identity
(\_ -> Nothing)
(\lookup prev target now start end future state ->
if Time.thisBeforeThat now end && (startTime target == end) then
Just (startTime target)
else
state
)
timeline
{-| This is to account for a bug in `Time.every` where it uses the provided delay time as a key to keep track of a time.
To get around this, we encode the current time as a really small part of the given time.
-}
encodeStamp : Time.Absolute -> Time.Absolute -> Maybe { delay : Float, target : Time.Posix }
encodeStamp now target =
let
millis =
Time.inMilliseconds target - nowInMillis
nowInMillis =
Time.inMilliseconds now
nowTail =
nowInMillis / 1000000
pingDelay =
millis + (1 / nowTail)
in
if pingDelay <= 0 then
Nothing
else
Just
{ delay = pingDelay
, target =
Time.millisToPosix (round (Time.inMilliseconds target + 1))
}
{-| -}
needsUpdate : Timeline event -> Bool
needsUpdate ((Timeline timeline) as tl) =
case timeline.queued of
Nothing ->
case timeline.interruption of
[] ->
timeline.running
_ ->
True
Just _ ->
True
{-| -}
hasChanged : Timeline event -> Bool
hasChanged (Timeline timeline) =
case timeline.queued of
Nothing ->
case timeline.interruption of
[] ->
False
_ ->
True
Just _ ->
True
{-| -}
justInitialized : Timeline event -> Bool
justInitialized (Timeline timeline) =
case timeline.now of
Quantity.Quantity qty ->
qty == 0

View File

@ -1,10 +1,10 @@
module InternalAnim.Transition exposing
( Transition(..)
, linear, standard, wobble, bezier
, initialVelocity, atX
, split, before
, hash, keyframes
, atX2, isStandard, takeAfter, withVelocities
, atX
, isStandard
, takeAfter, withVelocities
)
{-|
@ -13,12 +13,14 @@ module InternalAnim.Transition exposing
@docs linear, standard, wobble, bezier
@docs initialVelocity, atX
@docs split, before
@docs hash, keyframes
@docs atX
@docs isStandard
@docs takeAfter, withVelocities
Current bezier formats for elm-animator
Standard:
@ -62,11 +64,7 @@ Goals:
import Bezier
import Bezier.Spring as Spring
import InternalAnim.Duration as Duration
import InternalAnim.Hash as Hash
import InternalAnim.Quantity as Quantity
import InternalAnim.Time as Time
import InternalAnim.Units as Units
{-| A transition are all the bezier curves between A and B that we want to transition through.
@ -197,76 +195,15 @@ isStandard trans =
False
type alias TimeDomain =
{ start : PointInTime
, end : PointInTime
}
type alias PointInTime =
{ x : Time.Absolute
, y : Units.Pixels
}
{-| -}
atX :
Float
-> TimeDomain
-> Units.PixelsPerSecond
-> Units.PixelsPerSecond
-> Transition
->
{ position : Units.Pixels
, velocity : Units.PixelsPerSecond
}
atX progress domain introVelocity exitVelocity transition =
case transition of
Transition spline ->
if domain.start.x == domain.end.x then
{ position = domain.end.y
, velocity =
exitVelocity
}
else
spline
|> inTimeDomain domain introVelocity exitVelocity
|> posVel (toTimeProgress domain progress)
Wobble wob ->
let
totalX =
Time.inMilliseconds domain.end.x - Time.inMilliseconds domain.start.x
params =
Spring.new
{ wobble = wob.wobble
, quickness = wob.quickness
, settleMax = totalX
}
in
Spring.at
{ spring = params
, initial =
{ position = Units.inPixels domain.start.y
, velocity = Units.inPixelsPerMs introVelocity
}
, target = Units.inPixels domain.end.y
}
(totalX * progress)
|> wrapUnits
{-| -}
atX2 :
Float
-> Transition
->
{ position : Bezier.Point
, velocity : Bezier.Point
}
atX2 progress transition =
atX progress transition =
case transition of
Transition spline ->
let
@ -327,146 +264,6 @@ withVelocities intro exit transition =
}
toTimeProgress :
TimeDomain
-> Float
-> Float
toTimeProgress domain factor =
let
start =
Time.inMilliseconds domain.start.x
end =
Time.inMilliseconds domain.end.x
in
((end - start) * factor) + start
wrapUnits state =
{ position =
Units.pixels state.position
, velocity =
Units.pixelsPerSecond (state.velocity * 1000)
}
posVel :
Float
-> Bezier.Spline
->
{ position : Units.Pixels
, velocity : Units.PixelsPerSecond
}
posVel progress spline =
let
current =
Bezier.atX progress spline
firstDeriv =
Bezier.firstDerivative spline current.t
in
{ position =
Units.pixels current.point.y
, velocity =
Units.pixelsPerSecond ((firstDeriv.y / firstDeriv.x) * 1000)
}
zeroVelocity : Float
zeroVelocity =
0
initialVelocity : Transition -> Float
initialVelocity transition =
case transition of
Transition spline ->
let
firstDeriv =
-- at t == 0, the first derivative vector will always be 0,0
-- so we cheat in slightly.
Bezier.firstDerivative spline 0.001
in
if firstDeriv.x == 0 then
zeroVelocity
else
firstDeriv.y / firstDeriv.x
Wobble wob ->
zeroVelocity
type alias Domain =
{ start : Bezier.Point
, end : Bezier.Point
}
third : Float
third =
1 / 3
negativeThird : Float
negativeThird =
-1 / 3
{-| Note, we only rotate the control point to match the desired velocity.
However, there is the question of the magnitude of the control point.
I _think_ the magnitude is roughly equivalent to momentum.
It's possible that we override the built-in control points when there is a non-0 intro/exit Velocity.
Maybe it's a constant like 1/3 or something....
-}
toDomain : Domain -> Float -> Float -> Bezier.Spline -> Bezier.Spline
toDomain domain introVelocity exitVelocity spline =
let
{ two, three } =
toBezierPoints spline
totalX =
domain.end.x - domain.start.x
totalY =
domain.end.y - domain.start.y
ctrl1 =
let
angle =
atan2 introVelocity 1
in
{ x =
(totalX * two.x) + domain.start.x
, y =
(totalY * two.y) + domain.start.y
}
|> rotateAround angle domain.start
ctrl2 =
let
angle =
atan2 exitVelocity 1
in
{ x =
(totalX * three.x) + domain.start.x
, y =
(totalY * three.y) + domain.start.y
}
|> rotateAround angle domain.end
in
Bezier.fromPoints
domain.start
ctrl1
ctrl2
domain.end
toBezierPoints : Bezier.Spline -> { one : Bezier.Point, two : Bezier.Point, three : Bezier.Point, four : Bezier.Point }
toBezierPoints spline =
{ one = Bezier.first spline
@ -476,136 +273,6 @@ toBezierPoints spline =
}
inTimeDomain : TimeDomain -> Units.PixelsPerSecond -> Units.PixelsPerSecond -> Bezier.Spline -> Bezier.Spline
inTimeDomain domain introVelocity exitVelocity spline =
let
{ one, two, three, four } =
toBezierPoints spline
totalX =
Time.inMilliseconds domain.end.x - Time.inMilliseconds domain.start.x
totalY =
Units.inPixels domain.end.y - Units.inPixels domain.start.y
ctrl1 =
let
angle =
atan2 (Units.inPixelsPerMs introVelocity) 1
in
{ x =
(totalX * two.x) + Time.inMilliseconds domain.start.x
, y =
(totalY * two.y) + Units.inPixels domain.start.y
}
|> rotateAroundTimePoint angle domain.start
ctrl2 =
let
angle =
atan2 (Units.inPixelsPerMs exitVelocity) 1
in
{ x =
(totalX * three.x) + Time.inMilliseconds domain.start.x
, y =
(totalY * three.y) + Units.inPixels domain.start.y
}
|> rotateAroundTimePoint angle domain.end
in
Bezier.fromPoints
{ x = Time.inMilliseconds domain.start.x
, y = Units.inPixels domain.start.y
}
ctrl1
ctrl2
{ x = Time.inMilliseconds domain.end.x
, y = Units.inPixels domain.end.y
}
{-| -}
rotateAroundTimePoint : Float -> PointInTime -> Bezier.Point -> Bezier.Point
rotateAroundTimePoint radians center point =
let
centerX =
Time.inMilliseconds center.x
centerY =
Units.inPixels center.y
in
{ x = cos radians * (point.x - centerX) - sin radians * (point.y - centerY) + centerX
, y = sin radians * (point.x - centerX) + cos radians * (point.y - centerY) + centerY
}
{-| -}
rotateAround : Float -> Bezier.Point -> Bezier.Point -> Bezier.Point
rotateAround radians center point =
{ x = cos radians * (point.x - center.x) - sin radians * (point.y - center.y) + center.x
, y = sin radians * (point.x - center.x) + cos radians * (point.y - center.y) + center.y
}
translateBy : Bezier.Point -> Bezier.Point -> Bezier.Point
translateBy one two =
{ x = one.x + two.x
, y = one.y + two.y
}
scaleBy : Float -> Bezier.Point -> Bezier.Point
scaleBy k v =
{ x = k * v.x
, y = k * v.y
}
scaleAbout : Bezier.Point -> Float -> Bezier.Point -> Bezier.Point
scaleAbout p0 k p =
{ x = p0.x + k * (p.x - p0.x)
, y = p0.y + k * (p.y - p0.y)
}
{-| We are vector v and want the component in the direction d.
-}
componentIn : Bezier.Point -> Bezier.Point -> Float
componentIn d v =
v.x * d.x + v.y * d.y
zeroPoint : Bezier.Point
zeroPoint =
{ x = 0
, y = 0
}
scaleTo : Float -> Bezier.Point -> Bezier.Point
scaleTo q v =
let
largestComponent =
max (abs v.x) (abs v.y)
in
if largestComponent == 0 then
zeroPoint
else
let
scaledX =
v.x / largestComponent
scaledY =
v.y / largestComponent
scaledLength =
sqrt (scaledX * scaledX + scaledY * scaledY)
in
{ x = q * scaledX / scaledLength
, y = q * scaledY / scaledLength
}
{-| -}
hash : Transition -> String
hash transition =
@ -788,20 +455,6 @@ keyframeListFromNonNormalizedBezier steps toString str =
)
{-| -}
before : Float -> Transition -> Transition
before t transition =
transition
{-| -}
split : Float -> Transition -> { before : Transition, after : Transition }
split t transition =
{ before = transition
, after = transition
}
takeAfter : Float -> Transition -> Transition
takeAfter t transition =
case transition of

View File

@ -2,11 +2,9 @@ module InternalAnim.Units exposing
( Pixels
, PixelsPerSecond
, inPixels
, inPixelsPerMs
, inPixelsPerSecond
, pixels
, pixelsPerSecond
, zero
)
{-| -}
@ -22,30 +20,6 @@ type InPixels
= InPixels
zero =
Quantity.Quantity 0
type Unit guard
= Unit Float
type alias Position =
Unit Pos
type Pos
= Pos
type alias Velocity =
Unit Vel
type Vel
= Vel
type alias Pixels =
Quantity.Quantity Float InPixels
@ -59,11 +33,6 @@ inPixelsPerSecond (Quantity.Quantity pps) =
pps
inPixelsPerMs : PixelsPerSecond -> Float
inPixelsPerMs (Quantity.Quantity pps) =
1000 * pps
inPixels : Pixels -> Float
inPixels (Quantity.Quantity pixs) =
pixs