As a followup to my tutorial last year on Beautiful Code, Compelling Evidence, the tutorial on doing infovis and visual analytics in Haskell, I set about trying to do a pure-functional wrapper to Cairo. After a month or so of banging my and Conal’s head against it, I am ready to (with many MANY thanks to Conal Elliott for his help in getting me to think about this in a semantic way) release a first pass at purely functional 2D drawing, called Hieroglyph.
My original goal was to come up with something that simply produced no IO when creating a scene and was fast enough to create interactive visualizations. What I was eventually directed toward was semantic design and trying to figure out what a 2D visualization means. Hieroglyph is a first cut at that — a compromise between strict semantic design and staying close enough to Cairo to not make things easy in Hieroglyph that create performance problems in Cairo. Future versions of Hieroglyph or forks will be less based on Cairo and more based on semantic design, but for now, I’m satisfied with what I have.
So the first question is: What is a visualization? My answer is that it’s a function from data to a visual. A visual is an unstructured collection (bag) of primitives. The data can be any data that is interesting to the user doing the visualizing.
To encapsulate the Visual, we have:
type BaseVisual = [Primitive] class Visual a where primitives :: a -> BaseVisual
In Graphics.Rendering.Hieroglyph.Visual, I define instances on each of the basic collection types in the Haskell standard library, Visual v => Data.Set.Set v, Data.IntMap.IntMap v, Data.Map.Map a v, [v], and v itself. This allows the programmer to be unconcerned with how the collection of visual primitives is structured, allowing in many cases a straightforward map from data to primitives with the same structure. In addition, there are two relationships between Visuals that are important to describe. One is occlusion. To describe the occlusion characteristics of two visuals, I have defined the combinator “a `over` b” to describe a composed visual in which a occludes b. The other is specificity. There is a concept in visualization of level-of-detail, and this is encapsulated by the combinator “a `moreSpecific` b”, which declares that a is more specific than b. Both over and moreSpecific describe partial orderings over primitives, which can be used for filtering, or used by the renderer to order and constrain drawing.
Now for the primitives. A primitive is nothing more than a declaration of attributed geometry. Our primitives in Hieroglyph are Arc, Path, Rectangle, Text, Union, Image, and Hidden. I chose to implement primitives as “labeled” functions using the Haskell record modification syntax. While you can declare a primitive using the type constructor, it is easier and more expedient to use the lower case version of each of the primitive constructors and modify them using the record modification syntax. The Haddock documentation describes what each record field is for every primitive.
Over the next few days, I’ll be giving examples of Hieroglyph and talking a little bit more about my design process as I do. In the meantime, enjoy Hieroglyph and let me know about any bugs!