Beautiful Code [37]
When designing this library, I had to address the following issues:
Open-ended nature of the problem
There are already a large number of genome annotation types, and the number is growing daily. While many annotations can be drawn with simple rectangles of different colors, many of them—particularly the quantitative ones—can be quite complex to represent. Furthermore, different bioinformaticians may want to represent the same annotation type differently; for example, there are many different ways of representing genes, each best suited for different circumstances.
In order to accommodate the open-ended nature of this problem, I wanted to make it easy to add new visual representations to Bio::Graphics and to extend existing ones. Each representation should be highly configurable; for example, the programmer should be able to exercise exquisite control over the height, weight, boundary color, and fill color of even the simple rectangle. Furthermore, the programmer should be able to alter how each feature is rendered on a case-by-case basis.
Feature density
Some genomic features are very dense, whereas others are more sparse (compare the "Genes" and "WABA alignments" tracks in Figure 12-1). Features can also overlap spatially. Sometimes you want overlapping features to partially obscure each other, and sometimes you'd like to practice collision control, shifting the overlapping features up or down vertically to make them distinct. To add to the problem, features can contain subfeatures that themselves overlap, so collision control and spatial layout need to work in a recursive fashion.
I thought it would be good if collision control could be activated and deactivated in a context-dependent manner. For example, if there are thousands of overlapping features in the region being displayed, collision control might cause a track to become unmanageably busy and should be turned off. The programmer should be able to control at what point this context-sensitive collision control kicks in, or override it entirely.
Handling scale
I wanted Bio::Graphics to be able to draw pictures of a highly detailed 500 bp region of the genome, as well as a whole-genome view spanning 150 million nucleotides. To do this, the visual representation of features needs to change intelligently according to the current scale. At a scale of 500 bp, you can show the internal structure of a gene. At a scale of 1,000,000 bp, you can show only the start and end positions of the gene. At a scale of 100,000,000 bp, genes merge into a single black bar, and you should shift to a representation that shows the gene density as an intensity grayscale.
Useful for interactive web applications
I wanted Bio::Graphics to be suitable for the backend of an interactive web-based genome browser. In particular, I wanted end users to be able to click on the graphical renditions of features in order to bring up pull-down menus, link to other web pages, view tool tips, and so on. To accomplish this, Bio::Graphics had to be relatively fast so as to generate images in real time. It also had to keep track of the positions of each of its rendered features (and, if necessary, their subfeatures) in order to create an image map that could be passed back to the programmer.
Independence from graphic formats
I wanted Bio::Graphics to be useful for generating screen-quality, low-resolution images suitable for embedding in a web page, as well as for generating high-resolution, publication-quality images. To do this, the part of Bio::Graphics that handled the logic of the layout had to be separate from the part that generated graphics. Ideally, it should be able to output both pixmap and vector