Here we will continue the previous ideas about vectors and take a look at basic rendering of Vectors – for which we will use the Haskell Gloss package at https://hackage.haskell.org/package/gloss

Gloss claims that

*“Gloss hides the pain of drawing simple vector graphics behind a nice data type and a few display functions. Gloss uses OpenGL under the hood, but you won’t need to worry about any of that. Get something cool on the screen in under 10 minutes.”*

and to be fair I found it very easy to use but not without problems when installing. However I believe these problems are primarily macOS related and not due to issues with Gloss. I found that no matter what I did I could not get gloss to work from within ghci – it always gave this error

* ‘Dynamics.hs: user error (unknown GLUT entry glutInit)’*

But using stack to run ghci is fine if in stack.yaml you add

ghc-options:

GLUT: -optl-Wl,-framework,GLUT

and then doing ‘stack ghci’ everything should be ok.

#### Using Gloss

At the core of Gloss is the Picture type.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
-- | A blank picture, with nothing in it. blank :: Picture blank = Blank -- | A convex polygon filled with a solid color. polygon :: Path -> Picture polygon = Polygon -- | A line along an arbitrary path. line :: Path -> Picture line = Line -- | A circle with the given radius. circle :: Float -> Picture circle = Circle -- | A circle with the given thickness and radius. -- If the thickness is 0 then this is equivalent to `Circle`. thickCircle :: Float -> Float -> Picture thickCircle = ThickCircle -- Etc. Etc... |

and what’s really nice is that Picture is also a Monoid and so Picture can be combined and the result is also a Picture. This allows for a simple interface for rendering images.

Here’s a very simple program that will display x and y axes over a grey background in a large window.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS -Wall -fno-warn-type-defaults #-} import Graphics.Gloss import Data.Monoid window :: Display window = InWindow "Window" (1400, 800) (10, 10) background :: Color background = greyN 0.7 axes :: Picture axes = color red (line [ (-10000, 0), (10000, 0) ]) <> color red (line [ (0, -10000), (0, 10000) ]) main :: IO () main = display window background axes |

We have a window located at (10,10) of size (1400, 800). Its background is greyish (0 would be black and 1 would be white). The axes are two lines, both red. Both ranging from +/- 10000. Notice that color red (line [ (-10000, 0), (10000, 0) ]) is of type Picture and so too is color red (line [ (0, -10000), (0, 10000) ]) and that they are combined with the Monoid mappend operator <> to give a composite Picture.

If we are to write a Haskell module to render our vectors using Gloss then some points to note:

- We will need functions to convert a Vector into something that Gloss understands for rendering
- Vectors are 3-D and Gloss is 2-D and so we will be ignoring the z component in Vector.
- A Vector has a magnitude and a direction but no intrinsic notion of position. So when rendering we will either assume the location to be the origin or supply a position vector along with the vector to render.
- To indicate the direction the rendering needs to include an arrow at the relevant end.

This function

1 2 3 4 5 |
lineVector :: V.Vector -> V.Vector -> Picture lineVector pv@(V (xp, yc, _)) v@(V (x, y, _)) = Line [(xp, yc), (x, y)] <> arrowHead pv v |

takes a Vector v at position pv and creates a Line between the two points. Then, using the Monoid <> operator, combines the Line with an arrow head. The arrow head is provided by this function.

1 2 3 4 5 |
arrowHead :: V.Vector -> V.Vector -> Picture arrowHead pv v = polygonFromVectors [v, v ^+^ v', v ^+^ v''] where vdiff = neg . normalise $ v ^-^ pv v' = 10 *^ rotateXY (pi/8) vdiff v'' = 10 *^ rotateXY ( (-1)*pi/8) vdiff |

The *arrowHead* function is quite interesting and shows how using Vectors abstracts away much of the details of coordinates when dealing with points and lines in space. The *polygonFromVectors* function draws a filled in polygon using the list of Vectors as points in space. The *arrowHead* function takes the Vector v and ‘shifts’ it in such a way that the supplied position vector, pv, becomes the origin – i.e.
v ^-^ pv. The function
neg . normalise then ‘shrinks’ it to have a length of one unit and reverses it. So we have a unit vector in the opposite direction to the original. The lines

1 2 |
v' = 10 *^ rotateXY (pi/8) vdiff v'' = 10 *^ rotateXY ( (-1)*pi/8) vdiff |

then give the ‘left’ and ‘right’ parts of the arrow and finally the three Vectors
v, v ^+^ v', v ^+^ v'', which are the three points of the arrow head, are wrapped in a list and used by *polygonFromVectors* to actually do the drawing and fill in the arrow head. The function *rotateXY* is one I added to the Vectors module described in part 1. It’s just a 2-D rotation matrix expressed as a vector.

1 2 3 4 5 |
-- rotate in the X Y plane. rotateXY :: Scalar -> Vector -> Vector rotateXY theta (V (x, y, z )) = V (x', y', z) where x' = x * cos theta - y * sin theta y' = x * sin theta + y * cos theta |

Here’s the whole Haskell module.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS -Wall -fno-warn-type-defaults #-} module Views where import Graphics.Gloss import Vectors as V import Data.Monoid lineVector :: V.Vector -> V.Vector -> Picture lineVector pv@(V (xp, yc, _)) v@(V (x, y, _)) = Line [(xp, yc), (x, y)] <> arrowHead pv v lineVectorO :: V.Vector -> Picture lineVectorO = lineVector V.origin arrowHead :: V.Vector -> V.Vector -> Picture arrowHead pv v = polygonFromVectors [v, v ^+^ v', v ^+^ v''] where vdiff = neg . normalise $ v ^-^ pv v' = 10 *^ rotateXY (pi/8) vdiff v'' = 10 *^ rotateXY ( (-1)*pi/8) vdiff polygonFromVectors :: [V.Vector] -> Picture polygonFromVectors vs = Polygon pts where pts = map f vs where f (V (x, y, _) ) = (x, y) drawIt :: [Picture] -> IO () drawIt ps = display window background (axes <> mconcat ps) window :: Display window = InWindow "Window" (1400, 800) (10, 10) background :: Color background = greyN 0.7 axes :: Picture axes = color red (line [ (-10000, 0), (10000, 0) ]) <> color red (line [ (0, -10000), (0, 10000) ]) |

To try this out lets define a function to repeatedly draw a vector at the origin, rotate it and repeat.

1 2 3 4 5 6 |
vecsAtOrigin :: Int -> V.Vector -> IO () vecsAtOrigin n = drawPics . take n . map lineVectorO . iterate (V.rotateXY (2*pi / fromIntegral n)) |

This works by dividing the circle up into
(2*pi / fromIntegral n) pieces and iterating the rotate function. i.e.

iterate (V.rotateXY (2*pi / fromIntegral n)).

We then create a Picture type from each Vector in the (lazily infinite) list of vectors using
map lineVectorO. We then take however many we want from this list using
take n and then call
drawPics to render them. Simple, elegant and pleasing function composition.

Here’s some examples:

1 2 |
v = V (150, 0, 0) vecsAtOrigin 36 v |

or this one showing more vectors

I think that’s enough for the moment and again all of this is available on Github. Next time we’ll look at some simple dynamics equations expressed in Vector form and look at visualising their solutions.

Thanks for reading!

Aw, this was an incredibly good post. Taking the time and actual effort to make a top notch article… but what can I say… I put things off a lot and don’t seem to get anything done.

Thanks.

Hello there! This article couldn’t be written any better! Going through this article reminds me of my previous roommate! He continually kept preaching about this. I’ll send this information to him. Pretty sure he will have a very good read. I appreciate you for sharing!

Glad you liked it.

Way cool! Some very valid points! I appreciate you writing this post and the rest of the website is also really good.

Hi would you mind sharing which blog platform you’re using? I’m going to start my own blog soon but I’m having a tough time making a decision between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your layout seems different then most blogs and I’m looking for something completely unique. P.S My apologies for getting off-topic but I had to ask!

I’m using WordPress with what it calls the ‘Rainforest’ theme.