Hieroglyph Part II, basic mouse/keyboard interactivity

Written by Jeff Heard on February 3rd, 2009
screenshot-sample-interactive-app
> module Main where
> import Graphics.Rendering.Hieroglyph
> import Graphics.Rendering.Hieroglyph.Scaffolds.Interactive

This is a very very simple demonstration of how to use Hieroglyph in an interactive setting. We draw a red circle under the mouse point, a blue triangle which will cover the circle if the mouse goes over it, a line from the origin to the mouse, and a bit of text showing us where the mouse is at the moment.

Heiroglyph uses records to simulate labeled functions, where parameters can be given in any order or omitted with a sensible default. Here, we change attributes from the Cairo default, called “plain”, which is imported from Graphics.Rendering.Hieroglyph to give us some sensible stroke and fill colors.

The first is black and sets the outline color only

> blackstroke = plain{ strokeRGBA = black }

Next is blue, where the blue is slightly transparent (faded).

> bluefill    = plain{ fillRGBA = fade 0.8 blue, filled=True, outlined=False }

Next is red, and sets the fill color only

> redfill     = plain{ fillRGBA = red, filled=True, outlined=False }

Finally white, for our background rectangle, and sets our fill color only.

> whitefill   = plain{ fillRGBA = white, filled=True, outlined=False }

The next bit is our visualization. A visualization in Hieroglyph is a function that maps data to a collection of primitives. In this case, the data is simply the interactive scaffolding, an element from an infinite list of user interactions. The visualization function is then folded over the input data. More complicated data can be used by implementing the class Graphics.Rendering.Hieroglyph.Scaffolds.Interactive.Interactive, but for our purposes, we don’t care to track anything else but the mouse Position.

This is our type signature. Note that I go ahead and generalize the vis to all Interactive types rather than the Scaffolding, even though we’re using it. This is more portable, and more correct, because really the vis only depends on the mouse position, which can be gotten from any instance of Interactive.

Note that I use BaseVisual as the return type. This is the return type of “over,” which takes any two Visuals and returns a BaseVisual where the left operand is declared to be possibly occluding the right operand.

> vis :: Interactive a => a -> BaseVisual
> vis a =

Note that at no time do we draw anything in this function, nor does the order in which I declare these items actually matter. The only thing that matters here to order is the combinator `over`, which takes two Visuals and returns a new Visual that contains both operands, but the left operand occludes the right where there is overlap.

First, our line from the origin:

>     [path{ segments = [Line (getMousePosition a)], attribs = blackstroke }

Next our text, and you note that we don’t declare the over-ness here, meaning that the occlusion between these two primitives is undefined.

>     ,text{ str = "mousePosition" ++ (show . getMousePosition $ a), bottomleft = Point 10 (getSizeY a-40), attribs = blackstroke }]

Now we declare that these two primitives occlude the next primitive, a blue triangle.

>     `over` polygon{ begin = (Point 100 100), segments = [Line (Point 200 300), Line (Point 300 100)], attribs = bluefill}

And then we declare a mouse pointer tracking red dot that is below the triangle when the triangle and dot interact with each other.

>     `over` arc{ center = getMousePosition a, radius = 4, attribs = redfill}

And finally, we declare the background rectangle.

>     `over` rectangle{ width = getSizeX a, height = getSizeY a, attribs = whitefill }

Main is very simple in Hieroglyph, generally. Since we’re not reading any input files, all we do is create a basic 800 by 600 motion sensitive GUI. Note that the motion-sensitive part does hurt performance a little, so if you don’t need it, use the simpleGui function instead. Both take initial input data, the scene function from the data to the scene, and a title for the window.

> main = simpleMotionSensitiveGui scaffold vis "sample interactive app"

Voila, it’s an app! Just to recap, here’s the program without all the commentary in between:

module Main where

import Graphics.Rendering.Hieroglyph
import Graphics.Rendering.Hieroglyph.Scaffolds.Interactive 

blackstroke = plain{ strokeRGBA = black }
bluefill    = plain{ fillRGBA = fade 0.8 blue, filled=True, outlined=False }
redfill     = plain{ fillRGBA = red, filled=True, outlined=False }
whitefill   = plain{ fillRGBA = white, filled=True, outlined=False }

vis :: Interactive a => a -> BaseVisual
vis a =
     [path{ segments = [Line (getMousePosition a)], attribs = blackstroke }
     ,text{ str = "mousePosition" ++ (show . getMousePosition $ a), bottomleft = Point 10 (getSizeY a-40), attribs = blackstroke }]
     `over` polygon{ begin = (Point 100 100), segments = [Line (Point 200 300), Line (Point 300 100)], attribs = bluefill}
     `over` arc{ center = getMousePosition a, radius = 4, attribs = redfill}
     `over` rectangle{ width = getSizeX a, height = getSizeY a, attribs = whitefill }

main = simpleMotionSensitiveGui scaffold vis "sample interactive app"

Leave a Comment