Doing some basic calculus using Reactive Banana

后端 未结 2 1465
醉酒成梦
醉酒成梦 2021-01-18 17:19

Setup:

I am using Reactive Banana along with OpenGL and I have a gear that I want to spin. I have the following signals:

bTime :: Be         


        
相关标签:
2条回答
  • 2021-01-18 17:36

    Edit: to answer the question, yes, you're right to use the approximation you're using, it's Euler's method of solving a first order differential equation, and is accurate enough for your purposes, particularly since the user doesn't have an absolute value for the angular velocity lying around to judge you against. Decreasing your time interval would make it more accurate, but that's not important.

    You can do this in fewer, larger steps (see below), but this way seems clearest to me, I hope it is to you.

    Why bother with this longer solution? This works even when eDisplay happens at irregular intervals, because it calculates eDeltaT.

    Let's give ourselves a time event:

    eTime :: Event t Int
    eTime = bTime <@ eDisplay
    

    To get DeltaT, we'll need to keep track of the time interval passing:

    type TimeInterval = (Int,Int) -- (previous time, current time)
    

    so we can convert them to deltas:

    delta :: TimeInterval -> Int
    delta (t0,t1) = t1 - t0
    

    How should we update a time interval when we get a new one t2?

    tick :: Int -> TimeInterval -> TimeInterval
    tick t2 (t0,t1) = (t1,t2)
    

    So let's partially apply that to the time, to give us an interval updater:

    eTicker :: Event t (TimeInterval->TimeInterval)
    eTicker = tick <$> eTime
    

    and then we can accumE-accumulate that function on an initial time interval:

    eTimeInterval :: Event t TimeInterval
    eTimeInterval = accumE (0,0) eTicker
    

    Since eTime is measured since the start of rendering, an initial (0,0) is appropriate.

    Finally we can have our DeltaT event, by just applying (fmapping) delta on the time interval.

    eDeltaT :: Event t Int
    eDeltaT = delta <$> eTimeInterval
    

    Now we need to update the angle, using similar ideas.

    I'll make an angle updater, by just turning the bAngularVelocity into a multiplier:

    bAngleMultiplier :: Behaviour t (Double->Double)
    bAngleMultiplier = (*) <$> bAngularVelocity
    

    then we can use that to make eDeltaAngle: (edit: changed to (+) and converted to Double)

    eDeltaAngle :: Event t (Double -> Double)
    eDeltaAngle = (+) <$> (bAngleMultiplier <@> ((fromInteger.toInteger) <$> eDeltaT)
    

    and accumulate to get the angle:

    eAngle :: Event t Double
    eAngle = accumE 0.0 eDeltaAngle
    

    If you like one-liners, you can write

    eDeltaT = delta <$> (accumE (0,0) $ tick <$> (bTime <@ eDisplay)) where
        delta (t0,t1) = t1 - t0
        tick t2 (t0,t1) = (t1,t2)
    
    eAngle = accumE 0.0 $ (+) <$> ((*) <$> bAngularVelocity <@> eDeltaT) = 
    

    but I don't think that's terribly illuminating, and to be honest, I'm not sure I've got my fixities right since I've not tested this in ghci.

    Of course, since I made eAngle instead of bAngle, you need

    reactimate $ (draw gears) <$> eAngle
    

    instead of your original

    reactimate $ (draw gears) <$> (bAngle <@ eDisp)
    
    0 讨论(0)
  • 2021-01-18 17:36

    A simple approach is to assume that eDisplay happens at regular time intervals, and consider bAngularVelocity to be a relative rather than absolute measure, whch would give you the really rather short solution below. [Note that this is no good if eDisplay is out of your control, or if it fires visibly irregularly, or varies in regularity because it will cause your gear to rotate at different speeds as the interval of your eDisplay changes. You'd need my other (longer) approach if that's the case.]

    eDeltaAngle :: Event t (Double -> Double)
    eDeltaAngle = (+) <$> bAngularVelocity <@ eDisplay
    

    i.e. turn the bAngularVelocity into an adder Event that fires when you eDisplay, so then

    eAngle :: Event t Double
    eAngle = accumE 0.0 eDeltaAngle
    

    and finally

    reactimate $ (draw gears) <$> eAngle
    

    Yes, approximating the integral as a sum is appropriate, and here I'm further approximating by making possibly slightly inaccurate assumtions about the step width, but it's clear and should be smooth as long as your eDisplay is more-or-less regular.

    0 讨论(0)
提交回复
热议问题