Haskell 中的 SceneGraph 遍历
我想使用 Data.Tree
由 Transform
和 Shape
节点组成。在 SceneGraph 中,空间变换在遍历时累积并应用于形状以进行渲染。
type Transform = Vector2 Double
data Shape = Circle Double | Square Double
data SceneNode = XFormNode Transform | ShapeNode Shape
假设我们有一个场景,其中有一个物体移动到右侧,并由底部的正方形和顶部的圆形组成,
^
|
| ()
| []
0----->
我想出了这个树定义:
let tree = Node (XFormNode (vector2 10 0))
[Node (ShapeNode (Square 10)) []
,Node (XFormNode (vector2 0 10))
[Node (ShapeNode (Circle 10)) []]
]
渲染将是这样的:
render :: Position2 -> Shape -> IO ()
render p (Circle r) = drawCircle p r
render p (Square a) = drawSquare p a
我的问题是:
1)如何做您定义了一个 traverse 函数来累积转换并调用渲染任务吗?
2)如何避免进行遍历IO?
3)是否有一个更短的版本来定义这棵树?除了第一个 Node 定义和所有空 subForest 之外的所有内容实际上都是多余的。
谢谢你!
I want to implement a simple SceneGraph in Haskell using Data.Tree
consisting of Transform
and Shape
nodes. In a SceneGraph the spatial transformation is accumulated while traversing and applied to the shape for rendering.
type Transform = Vector2 Double
data Shape = Circle Double | Square Double
data SceneNode = XFormNode Transform | ShapeNode Shape
Say we have a scene with an object which is moved to the right and consisting of a Square at bottom and a Circle on top
^
|
| ()
| []
0----->
I came up with this tree definition:
let tree = Node (XFormNode (vector2 10 0))
[Node (ShapeNode (Square 10)) []
,Node (XFormNode (vector2 0 10))
[Node (ShapeNode (Circle 10)) []]
]
The rendering would be something like this:
render :: Position2 -> Shape -> IO ()
render p (Circle r) = drawCircle p r
render p (Square a) = drawSquare p a
My questions are:
1) How do you define a traverse
function, which accumulates the transformation and calls the render tasks?
2) How do you avoid making the traverse IO?
3) Is there a shorter version to define this tree? All but the first Node definition and all empty subForests are actually superfluous.
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
矛盾的是,
Data.Tree
在 Haskell 中并不经常使用,因为定义自定义树类型非常容易。在你的情况下,我将实现场景图(树)如下:你的例子变成
这个答案第3点。
要遍历树,请使用递归:
有更多通用的遍历可用,例如在
Data.Traversable,但它们更“统一”。简而言之,在树上使用递归是完全没问题的。
关于第 2 点,一旦您决定应该在 IO monad 中渲染圆形和正方形,您就无能为力了。
Paradoxically,
Data.Tree
is not used very often in Haskell because defining a custom tree type is so easy. In your case, I would implement the scene graph (tree) as follows:Your example becomes
This answers point 3.
To traverse the tree, use recursion:
There are more generic traversals available, for instance in
Data.Traversable
, but they are more "uniform". In short, using recursion on trees is perfectly fine.Concering point 2, there is nothing much you can do once you decide that circles and squares should be rendered in the
IO
monad.我喜欢将树表示为带有底层列表单子的单子列表。如果这句话令人困惑,只需看看代码:
这些树与
Data.Tree
中的树不同之处在于:您可以使用 monad 和列表的现有函数(例如
scanl
)。它们可以是一元的
ListT (ListT IO) FilePath
。请参阅 http://github.com/yairchu对于此示例,/generator/blob/master/ListTree/src/System/Directory/ListTree.hscons x mempty
是叶子,cons x (joinL [])
是具有 0 个子节点的节点。I like to represent trees as monadic lists with an underlying list monad. if this sentence is confusing, just look at the code:
These trees differ from those in
Data.Tree
in that:You can use the existing functions for monads and lists (like
scanl
) for these.They can be monadic
ListT (ListT IO) FilePath
. See http://github.com/yairchu/generator/blob/master/ListTree/src/System/Directory/ListTree.hs for this examplecons x mempty
is a leaf, andcons x (joinL [])
is a node with 0 children.