I am trying to make an n-body solver (a bunch of objects gravitationally attracted to each-other). The problem is that it looks like the gravity1 function does not feed back the return objects, resulting in linear motion of the objects:
The code looks like this:
updateGame :: Game -> SF AppInput Game
updateGame game =
proc input -> do
...
objs <- updateObjects' $ _foreground (Game._objects game) -< ()
...
updateObjects' :: [Object] -> SF () [Object]
updateObjects' objs = parB . fmap (updateObject1 objs ) $ objs
updateObject1 :: [Object] -> Object -> SF () Object
updateObject1 objs0 obj0 =
proc () -> do
obj <- gravity1 (objs0, obj0) -< ()
returnA -< obj
g = 6.673**(-11.0) :: Double
gravity1 :: ([Object], Object) -> SF () (Object)
gravity1 (objs0, obj0) =
proc () -> do
let
m0 = _mass obj0 :: Double
xform0 = (head . _transforms) obj0 :: M44 Double
p0 = ( view (_w._xyz)) xform0 :: V3 Double
ms = foldr1 (+) $ fmap (_mass) objs0 :: Double
xforms = fmap (head . _transforms) objs0 :: [M44 Double]
ps = foldr1 (^+^) $ fmap ( view (_w._xyz)) xforms :: V3 Double
dir = ps ^-^ p0 :: V3 Double
dist = norm dir :: Double
f = g * m0 * ms / dist**2.0 :: Double
acc = (f / ms) *^ (dir ^/ dist) :: V3 Double
s = 1000000000000000.0
--vel <- ((_velocity obj0) ^+^) ^<< integral -< (s *^ acc)
vel <- ((_velocity obj0) ^+^) ^<< integral -< (s *^ (DT.trace ("acc :" ++ show (s *^ acc)) $ acc))
let mtx =
mkTransformationMat
rot
tr
where
rot = (view _m33 xform0)
tr = vel + p0
returnA -< obj0 { _transforms = [mtx]
, _velocity = vel }
Running the code, there's the output I read in the console:
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
acc :V3 (-12105.49700148636) 12105.49700148636 0.0
acc :V3 12105.49700148636 12105.49700148636 0.0
acc :V3 NaN NaN NaN
Basically the values are all the same (the NaN's are due to object computed against itself, which I should fix, but that's not the issue here), it looks like the gravity1 function does not feed back the return object, despite return value being:
returnA -< obj0 { _transforms = [mtx]
, _velocity = vel }
The result is the linear motion of the objects, since acc seems to be a constant.
My expectation here is that aftergravity1 :: ([Object], Object) -> SF () (Object) has updated and returned the object, the updateObjects' objs = parB . fmap (updateObject1 objs ) $ objs and updateObject1 objs0 obj0 = ... returnA -< obj should result in all objects being updated and next iteration cycle should feed the gravity1 :: ([Object], Object) -> SF () (Object) with an updated set of objects, so that the value of acc is different every frame...
Am I misunderstanding the logic of how things are supposed to work here?
Let's consider the type of
gravity1:This is a function that, when provided with a list of
Objectand a particularObjectwill produce a signal function. This signal function is a so-called generator, meaning that it can be provided with empty input and continually produce a stream ofObjectoutput.Thus, in essence,
gravity1as two different kinds of inputs:([Object], Object), which it gets once and is forever unchanging, and()(which is basically equivalent to no streaming input).With this in mind, it does make sense that, once supplied with its static arguments,
gravity1will produce a constant stream ofObject— after all, it is never receiving updated data about where anyObjects are!In order to make the output stream dynamically react to changes in the positions of the objects, those positions need to be streaming, not static. In particular, the
[Object]input should be streaming:The other input argument, which specifies the starting position of the
Objectwe care about, probably doesn't need to be streaming, but it does need to be dealt with carefully (it is only a starting position after all).But if
gravity1takes the positions of theObjects as a streaming argument, how will you run it fromupdateObject? You will likely need to use some form of delay, such as:Incidentally, this strategy of using delay (i.e., the
reckeyword along withiPreor something similar) is exactly what you'll need to use ingravity1also to keep track of the current position of the particular object that you're calculating the gravity for.