使用反应性香蕉做一些基本的微积分 [英] Doing some basic calculus using Reactive Banana

查看:140
本文介绍了使用反应性香蕉做一些基本的微积分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

安装:



我使用反应香蕉和OpenGL一起使用,我有一个我想旋转的装置。我有以下信号:

  bTime :: Behavior t Int  - 渲染开始时的时间(以ms为单位)
bAngularVelosity :: Behavior t Double - 角速度
- 可以增加或
- 由用户减少
eDisplay :: Event t() - 需要重画屏幕
eKey :: Event t KeyState - 用户输入

最终,我需要计算 bAngle 然后过去到绘图函数:

  reactimate $(draw齿轮)< $> (bAngle< @ eDisp)

角度很容易计算: a =∫v(t)dt



问题

我想要做的就是将每个eDisplay事件的这个积分估计为 a =ΣvΔt(或者更常见的是if我需要)。这是正确的方式去做这件事吗?如果是这样,我怎么从 bTime
另请参阅
我怀疑答案使用 mapAccum 函数。如果是这样,请同时参阅我的其他问题

解决方案

编辑:回答问题,是的,您正确使用您使用的近似值,这是欧拉的解决方法一阶微分方程,并且对于你的目的来说足够精确,特别是因为用户没有角速度的绝对值来判断你的反应。减少你的时间间隔会使它更加准确,但这并不重要。



你可以用更少,更大的步骤做到这一点(见下文),但这种方式对我来说似乎最为清楚,我希望它对你有利。
$ b

为什么要用这个更长的解决方案呢?即使当 eDisplay 以不规则的间隔发生时,它也可以工作,因为它计算 eDeltaT



让我们给自己一个时间事件:

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

要获得DeltaT,我们需要跟踪传递的时间间隔:

 类型TimeInterval =(Int,Int) - (上一次,当前时间)
  delta :: TimeInterval  - > Int 
delta(t0,t1)= t1 - t0

我们应该如何更新时间间隔,当我们得到一个新的 t2

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

应用到那个时间,给我们一个间隔更新:

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

然后我们可以 accumE -accumulate该函数在初始时间间隔:

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

由于eTime是从渲染开始测量的,因此初始(0 ,0)是合适的。
$ b

最后,我们可以通过应用( fmap ping) delta来获得我们的DeltaT事件



  eDeltaT :: Event t Int 
eDeltaT = delta< / code> ; $> eTimeInterval

现在我们需要使用类似的想法来更新角度。



通过将 bAngularVelocity 转换为乘数,我将创建角度更新程序:

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

然后我们可以用它来制作 eDeltaAngle :(编辑:改为(+)并转换为 Double

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

和积累得到的角度:

  eAngle :: Event t Double 
eAngle = accumE 0.0 eDeltaAngle

如果您喜欢单线,您可以写下

<$ p $ (t 0,t 1)=($ t $ t $ t),其中
delta(t 0,t 1)= t1-t0
tick t2(t0,t1)=(t1,t2)

eAngle = accumE 0.0 $(+)$ $((*)< $> bAngularVelocity< @> eDeltaT)=

但我不认为这是非常明显的,说实话,我不知道我的固定性是正确的,因为我没有在ghci中测试过它。



当然,因为我做了<$ c $您需要

 <$ c>  $ c> reactimate $(绘制齿轮)< $> eAngle 

而不是原始的

  reactimate $(绘制齿轮)< $> (bAngle< @ eDisp)


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 :: Behavior t Int -- the time in ms from start of rendering
bAngularVelosity :: Behavior t Double -- the angular velocity
                                      -- which can be increase or
                                      -- decreased by the user
eDisplay :: Event t ()     -- need to redraw the screen
eKey :: Event t KeyState   -- user input

Ultimately, I need to calculate bAngle which is then past to the drawing function:

reactimate $ (draw gears) <$> (bAngle <@ eDisp)

The angle is easy to calculate: a = ∫v(t) dt

Question:

I think what I want to do is to approximate this integral as a = ∑ v Δt for each eDisplay event (or more often if I need to). Is this the correct way to go about this? If so, how do I get Δt from bTime?

See Also: I suspect that answer uses the mapAccum function. If so, please also see my other question as well.

解决方案

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)

这篇关于使用反应性香蕉做一些基本的微积分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆