[Haskell-cafe] Glome.hs-0.3 (bounding objects, heterogeneous
lists)
Jim Snow
jsnow at cs.pdx.edu
Mon Apr 28 18:49:59 EDT 2008
Andrew Coppin wrote:
>>> Well, for example, Haskell doesn't have hetrogenous lists - which
>>> are trivial in any OOP language. That's quite awkward to get around.
>>> Also, both spheres and cylinders have a "radius" property, but then
>>> you end up with name clashes. Again, a non-issue in OOP languages.
>>> [I gather there's some new GHC language extension to partially solve
>>> this one - but only when types are statically known. In the general
>>> case, you'd have to write a "radius class" and define instances...
>>> yuck!]
>>>
>> It's funny you mention that, those are actually problems I ran into,
>> but (having learned my lesson the hard way in Ocaml), I decided not
>> to try and force the language to do things my way, but instead try to
>> do things in a way that Haskell makes easy.
>> For instance, I started out by making each primitive a separate type
>> (Sphere, Triangle, etc...), and then made a type class that defines a
>> ray intersection method (and a few other odds and ends), but then I
>> realized that I can't do something as simple as store a list of
>> primitives (or if there is in fact a way, I'm not aware of it).
>> Instead, I made a single large sum type with all my primitives:
>> so that now I can easily make a list of primitives. (In fact, that's
>> what a Group is.) I also gave up on using named fields, since coming
>> up with a separate name for each one gets ugly: instead of radius,
>> you have sphere_radius, cylinder_radius, cone_radius disc_radius, etc...
>
> All of which works, but now it's a pain to add new primitives. And
> *all* supported primitives must be defined in a single monolithic
> module. And you can't reuse spheres as bounding volumes without either
> reimplementing them or loosing type safety.
The part about spheres and bounding spheres is actually not so much of a
problem: I implemented a general "Bound" primitive that can bound any
primitive with any other. In general, you would use a simple object
like a sphere or box as the left argument and a complex object as the
right argument.
data Solid = ...
| Bound Solid Solid
...
and then to intersect the "Bound" object, you first do a shadow test on
the left object before testing the right object:
rayint :: Solid -> Ray -> Flt -> Texture -> Rayint
rayint (Bound sa sb) r d t =
let (Ray orig _) = r
in if inside sa orig || shadow sa r d
then rayint sb r d t
else RayMiss
Some kinds of primitives aren't likely to work well for bounding since
they don't have a well-defined inside and outside (like triangles and
discs), but I'd rather provide maximum flexibility and assume that users
know how to use it sensibly.
http://www.haskell.org/haskellwiki/Glome_tutorial#Bounding_Objects
As for the maintenance issues, that is still a problem. It would be
nice to split all the individual primitives into their own modules.
Sebastian Sylvan wrote:
> On 4/27/08, Jim Snow <jsnow at cs.pdx.edu> wrote:
>
>> > For instance, I started out by making each primitive a separate type
>> > (Sphere, Triangle, etc...), and then made a type class that defines a
>> > ray intersection method (and a few other odds and ends), but then I
>> > realized that I can't do something as simple as store a list of
>> > primitives (or if there is in fact a way, I'm not aware of it).
>>
>
> You can, by using a "wrapper" type which wraps up any instance of the
> Intersect class:
>
> data Intersectable = forall a. Intersect a => MkIntersectable a
>
> For convenience you probably want to instantiate this wrapper in the
> class itself:
>
> instance Intersect Intersectable where
> rayIntersection (MkIntersectable x) ray = rayIntersection x ray
> boundingVolume (MkIntersectable x) = boundingVolume x
> -- etc...
>
> Now you can stick Intersectables in lists etc.
>
>
I think that sounds like what I ought to be doing.
-jim
More information about the Haskell-Cafe
mailing list