Beautiful Code [183]
After I added support for GD::SVG in Bio::Graphics, it became possible to produce SVGs simply by passing an -image_class argument to the Panel constructor:
$panel = Bio::Graphics::Panel->new(-length=>1000,
-width=>600,
-image_class => 'GD::SVG'
);
$panel->add_track.... etc...
print $panel->gd->svg;
Internally, the only change I had to make to Bio::Graphics was to process the -image_class option and to load the indicated image library. This allows for forward compatibility with new GD-compatible libraries. For example, if someone writes a GD::PDF that generates PDF-format graphic files, Bio::Graphics will be able to accommodate it.
12.3.3. Adding New Glyphs
At the time it was first published, Bio::Graphics supported about a dozen simple glyphs, including rectangles, ovals, arrows, the gene glyph, and a glyph that draws protein and DNA sequences. Each of these glyphs had multiple configuration options, leading to a very large number of possible displays. However, this number was still finite, whereas the number of feature types on the genome is potentially infinite. Fortunately, it is relatively easy to add new glyphs, and over time, I and other BioPerl developers have added many new glyphs to Bio::Graphics. Currently there are nearly 70 glyph types, ranging from whimsical (a Star of David) to sophisticated (a ternary plot for comparing frequencies of sequence variants in multiple populations).
The ability to easily extend existing glyphs to create new ones is a valuable feature. I will illustrate this in Example 12-5 by showing you how to create a new Glyph called hourglass.
Example 12-5. The hourglass glyph
1 package Bio::Graphics::Glyph::hourglass;
2 use strict;
3 use base 'Bio::Graphics::Glyph::box';
4 sub draw_component {
5 my $self = shift;
6 my ($gd,$dx,$dy) = @_;
7 my ($left,$top,$right,$bottom) = $self->bounds($dx,$dy);
8 # draw the hourglass as a polygon
9 my $poly = GD::Polygon->new;
10 $poly->addPt($left,$top);
11 $poly->addPt($right,$bottom);
12 $poly->addPt($right,$top);
13 $poly->addPt($left,$bottom);
14 $poly->addPt($left,$top);
15 $gd->filledPolygon($poly,$self->bgcolor);
16 $gd->polygon($poly,$self->fgcolor);
17 }
18 1;
This glyph generates an hourglass (Figure 12-5). The glyph starts by defining its package name, which by convention must begin with Bio::Graphics::Glyph:: (line 1). It then declares that it is inheriting from Bio::Graphics::Glyph::box, which is a simple glyph that draws a rectangle (line 3).
Figure 12-5. The hourglass glyph, a twisted version of the standard box glyph
The glyph then overrides the inherited draw_component() method (lines 4–17). The draw_ component() method is called by a draw() method of Bio::Graphics::Glyph after setting up the drawing environment. The method receives the GD object along with horizontal and vertical coordinates indicating the position of the glyph relative to its enclosing glyph. We pass the relative coordinates to the inherited bounds() method to convert them into the absolute coordinates of the rectangle enclosing the glyph (line 7).
Now we actually draw the glyph. We create a polygon using GD's polygon library and add vertices corresponding to the top-left, bottom-right, top-right, and bottom-left corners of the hourglass (lines 9–14). We then pass the polygon object first to the GD object's filledPolygon() method to draw the solid contents of the polygon (line 15), and then to the GD object's polygon() method to draw the outline of the hourglass (line 16). Notice our use of the inherited bgcolor() and fgcolor() methods to get the appropriate colors to use for the fill and the outline.
This demonstrates the simplicity of adding new glyphs to Bio::Graphics. In many cases, one can create a new glyph by inheriting from an existing glyph that does almost what one wants, and then modifying one or two methods to customize it.
Growing Beautiful Code in BioPerl > Conclusions