1 package Maasha::BGB::Draw;
3 # Copyright (C) 2009 Martin A. Hansen.
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # http://www.gnu.org/copyleft/gpl.html
22 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DESCRIPTION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
25 # Routines for creating Biopieces Browser graphics using Cairo and Pango.
28 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
39 use vars qw( @ISA @EXPORT );
41 @ISA = qw( Exporter );
44 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
49 # Martin A. Hansen, November 2009
51 # Given a list of features add these to
52 # a Cairo::Context object.
54 my ( $cr, # Cairo::Context object
55 $features, # List of features
60 my ( $feature, $first );
64 foreach $feature ( @{ $features } )
66 $cr->set_source_rgb( @{ $feature->{ 'color' } } );
68 if ( $feature->{ 'type' } eq 'line' )
70 $cr->set_line_width( $feature->{ 'line_width' } );
71 $cr->move_to( $feature->{ 'x1' }, $feature->{ 'y1' } );
72 $cr->line_to( $feature->{ 'x2' }, $feature->{ 'y2' } );
74 elsif ( $feature->{ 'type' } eq 'grid' )
76 $cr->set_line_width( $feature->{ 'line_width' } );
77 $cr->move_to( $feature->{ 'x1' }, $feature->{ 'y1' } );
78 $cr->line_to( $feature->{ 'x2' }, $feature->{ 'y2' } );
80 elsif ( $feature->{ 'type' } eq 'wiggle' )
82 $cr->set_line_width( $feature->{ 'line_width' } );
86 $cr->move_to( $feature->{ 'x1' }, $feature->{ 'y1' } );
91 $cr->line_to( $feature->{ 'x1' }, $feature->{ 'y2' } );
92 $cr->line_to( $feature->{ 'x2' }, $feature->{ 'y2' } );
94 elsif ( $feature->{ 'type' } eq 'arrow' )
96 draw_arrow_horizontal(
102 $feature->{ 'strand' },
106 elsif ( $feature->{ 'type' } eq 'rect' )
111 $feature->{ 'x2' } - $feature->{ 'x1' } + 1,
112 $feature->{ 'y2' } - $feature->{ 'y1' } + 1,
117 elsif ( $feature->{ 'type' } eq 'text' )
119 $cr->move_to( $feature->{ 'x1' }, $feature->{ 'y1' } );
120 $cr->set_font_size( $feature->{ 'font_size' } );
121 $cr->show_text( $feature->{ 'txt' } );
123 elsif ( $feature->{ 'type' } eq 'track_name' )
125 $cr->move_to( $feature->{ 'x1' }, $feature->{ 'y1' } );
126 $cr->set_font_size( $feature->{ 'font_size' } );
127 $cr->show_text( $feature->{ 'txt' } );
138 sub draw_arrow_horizontal
140 # Draws a horizontal arraw that
141 # consists of a shaft and arrow head.
143 my ( $cr, # Cairo::Context object
153 my ( $x_diff, $y_diff, $mid, $width, $s_width );
155 $x_diff = abs( $x2 - $x1 );
156 $y_diff = abs( $y2 - $y1 );
160 if ( $x_diff < $mid ) {
168 $cr->set_line_width( 1 );
170 if ( $strand eq '+' )
172 $cr->move_to( $x2 - $width, $y1 );
173 $cr->line_to( $x2, $y1 + $mid );
174 $cr->line_to( $x2 - $width, $y2 );
178 $cr->move_to( $x1 + $width, $y1 );
179 $cr->line_to( $x1, $y1 + $mid );
180 $cr->line_to( $x1 + $width, $y2 );
189 if ( $x_diff > $mid )
191 if ( $strand eq '+' ) {
192 $cr->rectangle( $x1, $y1, ( $x2 - $width ) - $x1, $y2 - $y1 );
194 $cr->rectangle( $x1 + $width, $y1, $x2 - ( $x1 + $width ), $y2 - $y1 );
205 # Martin A. Hansen, November 2009.
207 # Given a color number, pick that color from
208 # the color palette and return.
210 my ( $i, # color number
215 my ( $palette, $color );
248 $color = $palette->[ $i ];
250 map { $_ /= 255 } @{ $color };
258 # Martin A. Hansen, March 2010.
260 # Given a list of BGB tracks, render the
261 # PNG surface stream and return the base64
264 my ( $width, # image width
265 $height, # image height
266 $tracks, # list of BGB tracks to render
271 my ( $surface, $cr, $track, $png_data );
273 $surface = Cairo::ImageSurface->create( 'argb32', $width, $height );
274 $cr = Cairo::Context->create( $surface );
276 $cr->rectangle( 0, 0, $width, $height );
277 $cr->set_source_rgb( 1, 1, 1 );
280 foreach $track ( @{ $tracks } ) {
281 draw_feature( $cr, $track ) if $track;
284 $png_data = base64_png( $surface );
292 # Martin A. Hansen, March 2010.
294 # Given a list of BGB tracks, render these and save
297 my ( $file, # path to PDF file
298 $width, # image width
299 $height, # image height
300 $tracks, # list of BGB tracks to render
305 my ( $surface, $cr, $track );
307 $surface = Cairo::PdfSurface->create ( $file, $width, $height );
308 $cr = Cairo::Context->create( $surface );
310 $cr->rectangle( 0, 0, $width, $height );
311 $cr->set_source_rgb( 1, 1, 1 );
314 foreach $track ( @{ $tracks } ) {
315 draw_feature( $cr, $track ) if $track;
322 # Martin A. Hansen, March 2010.
324 # Given a list of BGB tracks, render these and save
327 my ( $file, # path to PDF file
328 $width, # image width
329 $height, # image height
330 $tracks, # list of BGB tracks to render
335 my ( $surface, $cr, $track );
337 $surface = Cairo::SvgSurface->create ( $file, $width, $height );
338 $cr = Cairo::Context->create( $surface );
340 $cr->rectangle( 0, 0, $width, $height );
341 $cr->set_source_rgb( 1, 1, 1 );
344 foreach $track ( @{ $tracks } ) {
345 draw_feature( $cr, $track ) if $track;
352 # Martin A. Hansen, October 2009.
354 # Prints a Cairo::Surface object to a PNG file.
356 my ( $surface, # Cairo::Surface object
357 $file, # path to PNG file
362 $surface->write_to_png( $file );
368 # Martin A. Hansen, December 2009.
370 # Extract a PNG stream from a Cairo::Surface object
371 # and convert it to base64 before returning it.
373 my ( $surface, # Cairo::Surface object
378 my ( $png_data, $callback, $base64 );
382 $callback = sub { $png_data .= $_[ 1 ] };
384 $surface->write_to_png_stream( $callback );
386 $base64 = MIME::Base64::encode_base64( $png_data );
392 sub get_distinct_colors
394 # Martin A. Hansen, November 2003.
396 # returns a number of distinct colors
401 # returns triplet of colors [ 0, 255, 127 ]
403 my ( $num_discrete, @color_vals, $c, @colors, $r_idx, $g_idx, $b_idx, $i );
405 $num_discrete = POSIX::ceil( $num_colors ** ( 1 / 3 ) );
408 $c = 1 - ($_ / ($num_discrete - 1) );
409 $c < 0 ? 0 : ($c > 1 ? 1 : $c);
410 } 0 .. $num_discrete;
412 ( $r_idx, $g_idx, $b_idx ) = ( 0, 0, 0 );
414 foreach $i ( 1 .. $num_colors )
416 push @colors, [ @color_vals [ $r_idx, $g_idx, $b_idx ] ];
418 if ( ++$b_idx >= $num_discrete )
420 if ( ++$g_idx >= $num_discrete ) {
421 $r_idx = ( $r_idx + 1 ) % $num_discrete;
424 $g_idx %= $num_discrete;
427 $b_idx %= $num_discrete;
430 @colors = map { [ int( $_->[ 0 ] * 255), int( $_->[ 1 ] * 255), int( $_->[ 2 ] * 255 ) ] } @colors;
432 return wantarray ? @colors : \@colors;
436 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<