Haskell 用反应式计算器做一些基本的微积分
设置: 我正在使用反应香蕉和OpenGL,我有一个齿轮,我想旋转。我有以下信号:Haskell 用反应式计算器做一些基本的微积分,haskell,reactive-banana,Haskell,Reactive Banana,设置: 我正在使用反应香蕉和OpenGL,我有一个齿轮,我想旋转。我有以下信号: bTime :: Behavior t Int -- the time in ms from start of rendering bAngularVelosity :: Behavior t Double -- the angular velocity -- which can be increase or
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
最后,我需要计算手镯
,然后通过绘图功能:
reactimate $ (draw gears) <$> (bAngle <@ eDisp)
reactimate$(牵引齿轮)(bAngle编辑:回答这个问题,是的,你使用的近似值是正确的,这是Euler解一阶微分方程的方法,对于你的目的来说足够精确,特别是因为用户没有周围角速度的绝对值来判断你Interval会使它更准确,但这并不重要
你可以用更少、更大的步骤来完成(见下文),但这种方式对我来说似乎是最清晰的,我希望对你来说是这样
为什么要费心使用这个较长的解决方案呢?即使eDisplay
以不规则的间隔出现,它也可以工作,因为它计算eDeltaT
让我们给自己一个时间事件:
eTime :: Event t Int
eTime = bTime <@ eDisplay
因此,我们可以将它们转换为三角洲:
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
由于时间是从渲染开始测量的,因此初始(0,0)
是合适的
最后,我们只需在时间间隔上应用(fmap
ping)delta
,就可以得到DeltaT事件
eDeltaT :: Event t Int
eDeltaT = delta <$> eTimeInterval
然后我们可以使用它来制作eDeltaAngle
:(编辑:更改为(+)
,并转换为双精度
)
如果你喜欢一行,你可以写
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) =
而不是你原来的
reactimate $ (draw gears) <$> (bAngle <@ eDisp)
reactimate$(draw gears)(手镯一种简单的方法是假设eDisplay
以固定的时间间隔发生,
并考虑<代码> BangalValue是相对的而不是绝对的度量,WHCH会给你一个非常短的解决方案。[请注意,如果eDisplay
超出您的控制范围,或者如果它明显不规则地发射,或者规则性地变化,这是不好的,因为随着eDisplay
间隔的变化,它会导致您的档位以不同的速度旋转。如果是这种情况,您需要我的其他(更长)方法。]
eDeltaAngle :: Event t (Double -> Double)
eDeltaAngle = (+) <$> bAngularVelocity <@ eDisplay
最后
reactimate $ (draw gears) <$> eAngle
reactimate$(牵引齿轮)e角度
是的,将积分近似为一个和是合适的,在这里,我通过对步长做出可能有点不准确的假设来进一步近似,但这是清楚的,并且应该是平滑的,只要你的eDiscovery或多或少是规则的。如果你愿意,你可以通过让用户改变对于你的B时间,用与>示例相同的方式。@AndrewC,我想这是你想要的链接吗?是的。.缺点:低速时抖动,高速时图形引擎过载,丑陋。我收回我的建议:使用角速度要优雅得多。我应该弄清楚B时间
是挂钟时间。AndrewC你呢r第一个解决方案是最初的OpenGL作者所做的,它确实不平滑,尤其是在处理键盘/鼠标输入时。另外,请参阅我对您的回答,以获得制作eDeltaT
的更好的mapAccum
解决方案。
eAngle :: Event t Double
eAngle = accumE 0.0 eDeltaAngle
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) =
reactimate $ (draw gears) <$> eAngle
reactimate $ (draw gears) <$> (bAngle <@ eDisp)
eDeltaAngle :: Event t (Double -> Double)
eDeltaAngle = (+) <$> bAngularVelocity <@ eDisplay
eAngle :: Event t Double
eAngle = accumE 0.0 eDeltaAngle
reactimate $ (draw gears) <$> eAngle