The 3D features of diagrams are changing even more rapidly than the rest of the library. The examples in this tutorial may not work in the next release! This tutorial is provided for those interested in trying out cutting-edge features; we welcome feedback and contributions.

Diagrams can be used to model three dimensional scenes, as well. There is currently one 3D Backend, diagrams-povray, which uses POV-Ray to raytrace the scene to PNG. (Note that diagrams-povray is currently unreleased; you can obtain and build it from github.) Many functions used for 2D diagrams have polymorphic types and can also be used in 3D. Some are specific to three dimensions—many of these are introduced below.

Every 3D scene rendered by diagrams-povray needs 3 elements: an object, a light, and a camera. (Other backends may work differently.) Without a light, the scene will appear completely black. The primitive sphere is converted to a Diagram by skin, similar to how strokePath works in 2D.

> import           Diagrams.Backend.POVRay
> import           Diagrams.Prelude
> 
> cam = mm50Camera # translate (r3 (0,0,10))
> 
> xy :: Direction V3 Double
> xy = direction . r3 $ (-1, -1, -0.5)
> 
> light = parallelLight xy white
> 
> color :: Colour Double -> Diagram POVRay -> Diagram POVRay
> color c = diffuse 0.5 . ambient 0.1 . sc c
> 
> example :: Diagram POVRay
> example = centerX $ cat unitX
>   [skin sphere # color cyan, skin sphere # color green, skin sphere # color blue]
> 
> main :: IO ()
> main = putStrLn $ renderDia POVRay POVRayOptions $ mconcat [example, cam, light]

We introduce a camera, a light which casts parallel rays, and some spheres (of radius 1). We move the camera away from the origin, so that the spheres are in its field of view. The translate function is polymorphic, and takes a vector V3 Double because cameras are defined in V3 Double. Now we render our scene. At the command line:

cabal exec runhaskell 3dtutorial.hs > 3dtutorial.pov
povray -W640 -H480 3dtutorial.pov

After these commands you should have a file 3dtutorial.png:

Write more about cameras and lights.

Besides spheres, Diagrams provides a primitive cube (rectangular prisms), cone, and cylinder. Cones and cylinders are special cases of the more general frustum, which can have arbitrary radii at the two ends. (A cone has radius 0 at one end, and a cylinder has the same radius at each end.)

ThreeD.Transform defines many Transformations specific to three dimensions. For example, we could have defined the camera above as cam = mm50Camera # translateZ 10. The example below uses boxes, nonuniform scales and rotations to make a slightly more interesting scene.

> import           Data.Colour.Palette.ColorSet
> import           Diagrams.Backend.POVRay
> import           Diagrams.Prelude
> 
> cam = mm50Camera # translateZ 20
> 
> xy :: Direction V3 Double
> xy = direction . r3 $ (-1, -1, -0.5)
> 
> light = parallelLight xy white
> 
> s = skin cube # scaleY 1.6 # translateX 3 # transform (aboutX (0.25 @@ turn)) # diffuse 1.5 # ambient 0.1
> 
> color :: Double -> Diagram POVRay -> Diagram POVRay
> color theta = sc $ rybColor (floor $ theta * 24)
> 
> example :: Diagram POVRay
> example = mconcat
>             [transform (aboutZ (t @@ turn)) (s # color t) | t <- [0,1/8..7/8]]
> 
> main :: IO ()
> main = putStrLn $ renderDia POVRay POVRayOptions $ mconcat [example, cam, light]

These primitive objects can also be combined using the CSG operations union, intersection and difference. These work on the various primitives, before skin wraps them in a Diagram. The image below subtracts a sphere from a cone (with its apex towards the camera) to create a crescent.

> import           Diagrams.Backend.POVRay
> import           Diagrams.Prelude
> 
> cam = mm50Camera # translateZ 10
> 
> xy :: Direction V3 Double
> xy = direction . r3 $ (-1, -1, -0.5)
> 
> light = parallelLight xy white
> 
> color :: Colour Double -> Diagram POVRay -> Diagram POVRay
> color c = diffuse 0.5 . ambient 0.1 . sc c
> 
> example :: Diagram POVRay
> example = centerX . color blue . skin $ difference sphere (sphere # translate (V3 0.5 0 0.5))
> 
> main :: IO ()
> main = putStrLn $ renderDia POVRay POVRayOptions $ mconcat [example, cam, light]