ошибка Gloss / OpenGL Stack Overflow после drawPicture. при использовании анимации на основе рекурсии

Просто чтобы повозиться с библиотекой Haskell Gloss, я написал:

import Graphics.Gloss

data World = World { worldBugs :: [Picture] }

bug :: Point -> Float -> Picture
bug (x, y) s =
    let head = Translate x (y - s) $ Circle (s * 0.8)
        body = Translate x (y + s) $ Circle (s * 1.2)
    in  pictures [head, body]

main = play (InWindow "Animation Test" (400, 400) (100, 100)) white 10
    (World . map (\(n,b) -> Translate (n * 20) (n * 20) $ b) $ zip [0..] $ replicate 100 $ bug (0,0) 100)
    (\world -> pictures $ worldBugs world)
    (\event world -> world)
    (\time (World bs) -> World $ map (Rotate (time * 10)) bs)

В котором отображаются некоторые «жуки» (два круга, образующие голову и туловище), которые вращаются с течением времени. Проблема в том, что через пару секунд работы он вылетает с:

Gloss / OpenGL Stack Overflow "after drawPicture."
  This program uses the Gloss vector graphics library, which tried to
  draw a picture using more nested transforms (Translate/Rotate/Scale)
  than your OpenGL implementation supports. The OpenGL spec requires
  all implementations to have a transform stack depth of at least 32,
  and Gloss tries not to push the stack when it doesn't have to, but
  that still wasn't enough.

  You should complain to your harware vendor that they don't provide
  a better way to handle this situation at the OpenGL API level.

  To make this program work you'll need to reduce the number of nested
  transforms used when defining the Picture given to Gloss. Sorry.

что, если я правильно понимаю, в основном означает, что в конечном итоге в стек помещается слишком много преобразований, и он переполняется. Он отмечает, что это может быть аппаратное ограничение (у меня Surface 2 Pro), так что я SOL? Он не делает этого при использовании animate, но это, вероятно, связано с тем, что он не передает состояние каждый тик.

Если я собираюсь сделать игру, мне придется использовать play для передачи состояния на следующий такт; Я не могу основывать все на времени. Есть ли способ обойти это? Гугление ошибки почти ничего не дает.


person Carcigenicate    schedule 28.10.2014    source источник
comment
Я только что понял, что, делая это, я в основном создаю вложенный список преобразований. Я попробую просто изменить поля трансформатора.   -  person Carcigenicate    schedule 28.10.2014


Ответы (1)


Проблема в том, что при каждом "тикании" он дополнительно вкладывает изображение, оборачивая его в другое преобразование (что в конечном итоге вызывает переполнение). Чтобы решить эту проблему, я просто сохранил каждое из значений в объекте Entity, а затем применил преобразования один раз в fromEntity.

    {- LANGUAGE threaded -}
module GlossTest where

import Graphics.Gloss

data Entity = Entity { entRot :: Float, entTrans :: Point, entScale :: Point, entPict :: Picture }

data World = World { worldBugs :: [Entity] }

entTranslate :: Float -> Float -> Entity -> Entity
entTranslate x y (Entity r t s p) = Entity r (x,y) s p

entRotate :: Float -> Entity -> Entity
entRotate x (Entity r t s p) = Entity x t s p

entRotateBy :: Float -> Entity -> Entity
entRotateBy n (Entity r t s p) = Entity (r + n) t s p

entMove :: Float -> Float -> Entity -> Entity
entMove x y (Entity r (tX,tY) s p) = Entity r (tX + x, tY + y) s p

toEntity :: Picture -> Entity
toEntity = Entity 0 (0,0) 1

fromEntity :: Entity -> Picture
fromEntity (Entity r (tX,tY) (sX,sY) p) = Rotate r . Translate tX tY $ Scale sX sY p

bug :: Point -> Float -> Entity
bug (x, y) s =
    let head = Rotate 0 $ Translate x (y - s) $ Circle (s * 0.8)
        body = Rotate 0 $ Translate x (y + s) $ Circle (s * 1.2)
    in  toEntity $ pictures [head, body]

main = play
    (InWindow "Animation Test" (400, 400) (100, 100)) white 1
    (World . map (\(n,b) -> entTranslate (n * 1) (n * 1) $ b) $ zip [0..] $ replicate 10 $ bug (0,0) 100)
    (\world -> pictures . map fromEntity $ worldBugs world)
    (\event world -> world)
    (\time (World bs) -> World $ map (\(n,b) -> entRotateBy (n * time) $ entMove time time b) $ zip [0..] bs)
person Carcigenicate    schedule 28.10.2014