File Coverage

blib/lib/Bio/Phylo/Treedrawer/Abstract.pm
Criterion Covered Total %
statement 99 263 37.6
branch 31 94 32.9
condition 6 21 28.5
subroutine 12 27 44.4
pod n/a
total 148 405 36.5


line stmt bran cond sub pod time code
1             package Bio::Phylo::Treedrawer::Abstract;
2 3     3   19 use strict;
  3         6  
  3         80  
3 3     3   14 use Bio::Phylo::Util::Exceptions 'throw';
  3         7  
  3         144  
4 3     3   15 use Bio::Phylo::Util::Logger ':levels';
  3         6  
  3         8139  
5              
6             my $logger = Bio::Phylo::Util::Logger->new;
7             our $DEFAULT_FONT = 'Arial';
8             our @FONT_DIR;
9              
10             =head1 NAME
11              
12             Bio::Phylo::Treedrawer::Abstract - Abstract graphics writer used by treedrawer, no
13             serviceable parts inside
14              
15             =head1 DESCRIPTION
16              
17             This module is an abstract super class for the various graphics formats that
18             Bio::Phylo supports. There is no direct usage of this class. Consult
19             L for documentation on how to draw trees.
20              
21             =cut
22              
23             sub _new {
24 3     3   618 my $class = shift;
25 3         11 my %args = @_;
26             my $self = {
27             'TREE' => $args{'-tree'},
28             'DRAWER' => $args{'-drawer'},
29 3         16 'API' => $args{'-api'},
30             };
31 3         10 return bless $self, $class;
32             }
33 195     195   1041 sub _api { shift->{'API'} }
34 56     56   174 sub _drawer { shift->{'DRAWER'} }
35 9     9   95 sub _tree { shift->{'TREE'} }
36              
37             =begin comment
38              
39             Type : Internal method.
40             Title : _draw
41             Usage : $svg->_draw;
42             Function: Main drawing method.
43             Returns :
44             Args : None.
45              
46             =end comment
47              
48             =cut
49              
50             sub _draw {
51 3     3   7 my $self = shift;
52 3         14 my $td = $self->_drawer;
53 3         33 $self->_draw_scale;
54             $self->_tree->visit_depth_first(
55             '-post' => sub {
56 17     17   31 my $node = shift;
57 17         70 my $x = $node->get_x;
58 17         76 my $y = $node->get_y;
59 17         66 my $is_terminal = $node->is_terminal;
60 17 100       87 my $r = $is_terminal ? $td->get_tip_radius : $td->get_node_radius;
61 17         77 $self->_draw_branch($node);
62 17 50       526 if ( $node->get_collapsed ) {
63 0         0 $self->_draw_collapsed($node);
64             }
65             else {
66 17 100       52 if ( my $name = $node->get_name ) {
67 14         40 $name =~ s/_/ /g;
68 14         23 $name =~ s/^'(.*)'$/$1/;
69 14         23 $name =~ s/^"(.*)"$/$1/;
70 14 100       47 $self->_draw_text(
71             '-x' => int( $x + $td->get_text_horiz_offset ),
72             '-y' => int( $y + $td->get_text_vert_offset ),
73             '-text' => $name,
74             '-rotation' => [ $node->get_rotation, $x, $y ],
75             '-font_face' => $node->get_font_face,
76             '-font_size' => $node->get_font_size,
77             '-font_style' => $node->get_font_style,
78             '-font_colour' => $node->get_font_colour,
79             '-font_weight' => $node->get_font_weight,
80             'class' => $is_terminal ? 'taxon_text' : 'node_text',
81             );
82             }
83             }
84 17         997 $self->_draw_circle(
85             '-radius' => $r,
86             '-x' => $x,
87             '-y' => $y,
88             '-width' => $node->get_branch_width,
89             '-stroke' => $node->get_node_outline_colour,
90             '-fill' => $node->get_node_colour,
91             '-url' => $node->get_link,
92             );
93 17 50       103 if ( $node->get_clade_label ) {
94 0         0 $self->_draw_clade_label($node);
95             }
96             }
97 3         14 );
98 3         44 $self->_draw_pies;
99 3         52 $self->_draw_legend;
100 3         14 return $self->_finish;
101             }
102              
103             sub _draw_pies {
104 0     0   0 my $self = shift;
105 0         0 $logger->warn( ref($self) . " can't draw pies" );
106             }
107              
108             sub _draw_legend {
109 0     0   0 my $self = shift;
110 0         0 $logger->warn( ref($self) . " can't draw a legend" );
111             }
112              
113             sub _finish {
114 0     0   0 my $self = shift;
115 0         0 throw 'NotImplemented' => ref($self) . " won't complete its drawing";
116             }
117              
118             sub _draw_text {
119 0     0   0 my $self = shift;
120 0         0 throw 'NotImplemented' => ref($self) . " can't draw text";
121             }
122              
123             sub _draw_line {
124 0     0   0 my $self = shift;
125 0         0 throw 'NotImplemented' => ref($self) . " can't draw line";
126             }
127              
128             sub _draw_arc {
129 0     0   0 my $self = shift;
130 0         0 throw 'NotImplemented' => ref($self) . " can't draw arc";
131             }
132              
133             sub _draw_curve {
134 0     0   0 my $self = shift;
135 0         0 throw 'NotImplemented' => ref($self) . " can't draw curve";
136             }
137              
138             sub _draw_multi {
139 0     0   0 my $self = shift;
140 0         0 throw 'NotImplemented' => ref($self) . " can't draw multi line";
141             }
142              
143             sub _draw_triangle {
144 0     0   0 my $self = shift;
145 0         0 throw 'NotImplemented' => ref($self) . " can't draw triangle";
146             }
147              
148             sub _draw_rectangle {
149 0     0   0 my $self = shift;
150 0         0 throw 'NotImplemented' => ref($self) . " can't draw rectangle";
151             }
152              
153             # XXX incomplete, still needs work for the radial part
154             sub _draw_clade_label {
155 0     0   0 my ( $self, $node ) = @_;
156 0         0 $logger->info("drawing clade label ".$node->get_clade_label);
157 0         0 my $td = $self->_drawer;
158 0         0 my $tho = $td->get_text_horiz_offset;
159 0         0 my $tw = $td->get_text_width;
160            
161 0         0 my $lmtl = $node->get_leftmost_terminal;
162 0         0 my $rmtl = $node->get_rightmost_terminal;
163 0         0 my $root = $node->get_tree->get_root;
164 0         0 my $desc = $node->get_descendants;
165 0         0 my $ncl = scalar( grep { $_->get_clade_label } @{ $node->get_ancestors } );
  0         0  
  0         0  
166            
167             # copy font preferences, if any
168 0         0 my %font = ( '-text' => $node->get_clade_label );
169 0   0     0 my $f = $node->get_clade_label_font || {};
170 0         0 my @properties = qw(face size style weight colour);
171 0         0 for my $p ( @properties ) {
172 0 0       0 if ( my $value = $f->{"-$p"} ) {
173 0         0 $font{"-font_$p"} = $value;
174             }
175             else {
176 0         0 my $method = "get_font_$p";
177 0 0       0 if ( $value = $node->$method ) {
178 0         0 $font{"-font_$p"} = $value;
179             }
180             }
181             }
182            
183             # get cartesian coordinates for root and leftmost and rightmost tip
184 0         0 my ( $cx, $cy ) = ( $root->get_x, $root->get_y );
185 0         0 my ( $rx, $ry ) = ( $rmtl->get_x, $rmtl->get_y );
186 0         0 my ( $lx, $ly ) = ( $lmtl->get_x, $lmtl->get_y );
187            
188             # handle radial projection, either phylogram or cladogram
189 0 0       0 if ( $td->get_shape =~ /radial/i ) {
190            
191             # compute tallest node in the clade and radius from the root
192 0         0 my $radius;
193 0 0       0 if ( @$desc ) {
194 0         0 for my $d ( @$desc ) {
195            
196             # pythagoras
197 0         0 my ( $x1, $y1 ) = ( $d->get_x, $d->get_y );
198 0         0 my $h1 = sqrt( abs($cx-$x1)*abs($cx-$x1) + abs($cy-$y1)*abs($cy-$y1) );
199 0 0       0 $radius = $h1 if not defined $radius; # initialize
200 0 0       0 $radius = $h1 if $h1 >= $radius; # bump up if higher value
201             }
202             }
203             else {
204             # pythagoras
205 0         0 my ( $x1, $y1 ) = ( $node->get_x, $node->get_y );
206 0         0 $radius = sqrt( abs($cx-$x1)*abs($cx-$x1) + abs($cy-$y1)*abs($cy-$y1) );
207             }
208            
209             # compute angles and coordinates of start and end of arc
210 0         0 my $offset = $td->get_clade_label_width * $ncl;
211 0         0 $radius += ( $tho * 2 + $tw + $offset );
212 0         0 my ( $rr, $ra ) = $td->cartesian_to_polar( ($rx-$cx), ($ry-$cy) ); # rightmost
213 0         0 my ( $lr, $la ) = $td->cartesian_to_polar( ($lx-$cx), ($ly-$cy) ); # leftmost
214 0         0 my ( $x1, $y1 ) = $td->polar_to_cartesian( $radius, $ra ); # + add origin!
215 0         0 my ( $x2, $y2 ) = $td->polar_to_cartesian( $radius, $la ); # + add origin!
216            
217             # draw line and label
218 0         0 my $ntips = $node->get_terminals;
219 0         0 my $rtips = $root->get_terminals;
220 0 0       0 my $large = (scalar(@$ntips)/scalar(@$rtips)) > 1/2 ? 1 : 0; # spans majority of tips
221 0         0 $self->_draw_arc(
222             '-x1' => $x1 + $cx,
223             '-y1' => $y1 + $cy,
224             '-x2' => $x2 + $cx,
225             '-y2' => $y2 + $cy,
226             '-radius' => $radius,
227             '-large' => $large,
228             '-sweep' => 0,
229             );
230            
231             # include $tho
232 0         0 my ( $tx1, $ty1 ) = $td->polar_to_cartesian(($radius+$tho),$ra); # + add origin!
233 0         0 $self->_draw_text( %font,
234             '-x' => $tx1 + $cx,
235             '-y' => $ty1 + $cy,
236             '-rotation' => [ $ra, $tx1 + $cx, $ty1 + $cy ],
237             );
238             }
239            
240             # can do the same thing for clado and phylo
241             else {
242            
243             # fetch the tallest node in t
244 0         0 my $x1;
245 0         0 for my $d ( @$desc ) {
246 0         0 my $x = $d->get_x;
247 0 0       0 $x1 = $x if not defined $x1; # initialize
248 0 0       0 $x1 = $x if $x >= $x1; # bump if $higher
249             }
250            
251             # draw line and label
252 0         0 my $offset = $td->get_clade_label_width * $ncl;
253 0         0 $x1 += ( $tho * 2 + $tw + $offset );
254 0         0 my ( $y1, $y2 ) = ( $lmtl->get_y, $rmtl->get_y );
255 0         0 $self->_draw_line(
256             '-x1' => $x1,
257             '-x2' => $x1,
258             '-y1' => $y1,
259             '-y2' => $y2,
260             );
261 0         0 $self->_draw_text( %font,
262             '-x' => ($x1+$tho),
263             '-y' => $y1,
264             '-rotation' => [ 90, ($x1+$tho), $y1 ],
265             );
266             }
267            
268            
269             }
270              
271             sub _draw_collapsed {
272 0     0   0 $logger->info("drawing collapsed node");
273 0         0 my ( $self, $node ) = @_;
274 0         0 my $td = $self->_drawer;
275 0         0 $node->set_collapsed(0);
276              
277             # Get the height of the tallest node above the collapsed clade; for cladograms this
278             # is 1, for phylograms it's the sum of the branch lengths. Then, compute x1 and x2,
279             # i.e. the tip and the base of the triangle, which consequently are different between
280             # cladograms and phylograms.
281 0         0 my $tallest = 0;
282 0         0 my ( $x1, $x2 );
283 0         0 my $clado = $td->get_mode =~ m/clado/i;
284 0 0       0 if ( $clado ) {
285 0         0 $tallest = 1;
286 0         0 $x1 = $node->get_x - $tallest * $td->_get_scalex;
287 0         0 $x2 = $node->get_x;
288             }
289             else {
290             $node->visit_depth_first(
291             '-pre' => sub {
292 0     0   0 my $n = shift;
293 0         0 my $height = $n->get_parent->get_generic('height') + $n->get_branch_length;
294 0         0 $n->set_generic( 'height' => $height );
295 0 0       0 $tallest = $height if $height > $tallest;
296             }
297 0         0 );
298 0         0 $tallest -= $node->get_branch_length;
299 0         0 $x1 = $node->get_x;
300 0         0 $x2 = ( $tallest * $td->_get_scalex + $node->get_x );
301             }
302            
303             # draw the collapsed triangle
304 0         0 my $padding = $td->get_padding;
305 0         0 my $cladew = $td->get_collapsed_clade_width($node);
306 0         0 my $y1 = $node->get_y;
307 0         0 $self->_draw_triangle(
308             '-fill' => $node->get_node_colour,
309             '-stroke' => $node->get_node_outline_colour,
310             '-width' => $td->get_branch_width($node),
311             '-url' => $node->get_link,
312             'id' => 'collapsed' . $node->get_id,
313             'class' => 'collapsed',
314             '-x1' => $x1,
315             '-y1' => $y1,
316             '-x2' => $x2,
317             '-y2' => $y1 + $cladew / 2 * $td->_get_scaley,
318             '-x3' => $x2,
319             '-y3' => $y1 - $cladew / 2 * $td->_get_scaley,
320             );
321            
322             # draw the collapsed clade label
323 0 0       0 if ( my $name = $node->get_name ) {
324 0         0 $name =~ s/_/ /g;
325 0         0 $name =~ s/^'(.*)'$/$1/;
326 0         0 $name =~ s/^"(.*)"$/$1/;
327 0         0 $self->_draw_text(
328             'id' => 'collapsed_text' . $node->get_id,
329             'class' => 'collapsed_text',
330             '-font_face' => $node->get_font_face,
331             '-font_size' => $node->get_font_size,
332             '-font_style' => $node->get_font_style,
333             '-font_colour' => $node->get_font_colour,
334             '-font_weight' => $node->get_font_weight,
335             '-x' => int( $x2 + $td->get_text_horiz_offset ),
336             '-y' => int( $y1 + $td->get_text_vert_offset ),
337             '-text' => $name,
338             );
339             }
340 0         0 $node->set_collapsed(1);
341             }
342              
343             =begin comment
344              
345             Type : Internal method.
346             Title : _draw_scale
347             Usage : $svg->_draw_scale();
348             Function: Draws scale for phylograms
349             Returns :
350             Args : None
351              
352             =end comment
353              
354             =cut
355              
356             sub _draw_scale {
357 3     3   7 my $self = shift;
358 3         7 my $drawer = $self->_drawer;
359            
360             # if not options provided, won't attempt to draw a scale
361 3 100       15 if ( my $options = $drawer->get_scale_options ) {
362 2         10 my $tree = $self->_tree;
363 2         12 my $root = $tree->get_root;
364 2         12 my $rootx = $root->get_x;
365 2         9 my $height = $drawer->get_height;
366            
367             # read and convert the font preferences for the _draw_text method
368 2         6 my %font;
369 2 50 33     27 if ( $options->{'-font'} and ref $options->{'-font'} eq 'HASH' ) {
370 0         0 for my $key ( keys %{ $options->{'-font'} } ) {
  0         0  
371 0         0 my $nk = $key;
372 0         0 $nk =~ s/-/-font_/;
373 0         0 $font{$nk} = $options->{'-font'}->{$key};
374             }
375             }
376              
377             # convert width and major/minor ticks to absolute pixel values
378 2         9 my ( $major, $minor ) = ( $options->{'-major'}, $options->{'-minor'} );
379 2         6 my $width = $options->{'-width'};
380 2         4 my $blocks = $options->{'-blocks'};
381            
382             # find the tallest tip, irrespective of it being collapsed
383 2         4 my ($tt) = sort { $b->get_x <=> $a->get_x } @{ $tree->get_entities };
  14         55  
  2         7  
384 2         13 my $ttx = $tt->get_x;
385 2         19 my $ptr = $tt->calc_path_to_root;
386 2 50       14 if ( $width =~ m/^(\d+)%$/ ) {
387 2         14 $width = ( $1 / 100 ) * ( $ttx - $rootx );
388             }
389 2 50       8 if ( my $units = $options->{'-units'} ) {
390            
391             # now we need to calculate how much each branch length unit (e.g.
392             # substitutions) is in pixels. The $width then becomes the length
393             # of one branch length unit in pixels times $units
394 0         0 my $unit_in_pixels = ( $ttx - $rootx ) / $ptr;
395 0         0 $width = $units * $unit_in_pixels;
396             }
397 2 50       10 if ( $major =~ m/^(\d+)%$/ ) {
398 2         6 $major = ( $1 / 100 ) * $width;
399             }
400 2 50       10 if ( $minor =~ m/^(\d+)%$/ ) {
401 2         7 $minor = ( $1 / 100 ) * $width;
402             }
403 2 50 33     8 if ( $blocks and $blocks =~ m/^(\d+)%$/ ) {
404 0         0 $blocks = ( $1 / 100 ) * $width;
405             }
406            
407             # draw scale line and apply label
408 2 50       9 my $x1 = $options->{'-reverse'} ? $ttx : $rootx;
409 2 50       25 my $ws = $options->{'-reverse'} ? -1 : 1;
410 2 50       6 my $ts = $options->{'-reverse'} ? 0 : 1;
411 2         19 $self->_draw_line(
412             '-x1' => $x1,
413             '-y1' => ( $height - 40 ),
414             '-x2' => $x1 + ($width*$ws),
415             '-y2' => ( $height - 40 ),
416             'class' => 'scale_bar',
417             );
418             $self->_draw_text( %font,
419             '-x' => ( $x1 + ($width*$ts) + $drawer->get_text_horiz_offset ),
420             '-y' => ( $height - 30 ),
421 2   100     148 '-text' => $options->{'-label'} || ' ',
422             'class' => 'scale_label',
423             );
424            
425             # pre-compute indexes so we can reverse
426 2         151 my ( @maji, @mini, @blocksi, $j ); # major/minor/blocks indexes
427 2 50       8 if ( $options->{'-reverse'} ) {
428 0         0 for ( my $i = $ttx ; $i >= ( $ttx - $width ) ; $i -= $minor ) {
429 0 0       0 if ( not $j % sprintf('%.0f', $major/$minor) ) {
430 0         0 push @maji, $i;
431 0 0 0     0 if ( $blocks and not scalar(@maji) % 2 ) {
432 0         0 push @blocksi, $i;
433             }
434             }
435 0         0 push @mini, $i;
436 0         0 $j++;
437             }
438             }
439             else {
440 2         9 for ( my $i = $rootx ; $i <= ( $rootx + $width ) ; $i += $minor ) {
441 102 100       177 if ( not $j % sprintf('%.0f', $major/$minor) ) {
442 22         32 push @maji, $i;
443 22 50 33     37 if ( $blocks and not scalar(@maji) % 2 ) {
444 0         0 push @blocksi, $i;
445             }
446             }
447 102         136 push @mini, $i;
448 102         153 $j++;
449             }
450             }
451            
452             # draw ticks and labels
453 2         4 my $major_text = 0;
454 2         5 my $major_scale = ( $major / $width ) * $ptr;
455 2   50     16 my $tmpl = $options->{'-tmpl'} || '%s';
456 2 50   22   15 my $code = ref $tmpl ? $tmpl : sub { sprintf $tmpl, shift };
  22         114  
457 2         8 for my $i ( @maji ) {
458 22         68 $self->_draw_line(
459             '-x1' => $i,
460             '-y1' => ( $height - 40 ),
461             '-x2' => $i,
462             '-y2' => ( $height - 25 ),
463             'class' => 'scale_major',
464             );
465 22         1181 $self->_draw_text( %font,
466             '-x' => $i,
467             '-y' => ( $height - 5 ),
468             '-text' => $code->( $major_text ),
469             'class' => 'major_label',
470             );
471 22         1180 $major_text += $major_scale;
472             }
473 2         5 for my $i ( @mini ) {
474 102 50       5234 next if not $i % $major;
475 102         220 $self->_draw_line(
476             '-x1' => $i,
477             '-y1' => ( $height - 40 ),
478             '-x2' => $i,
479             '-y2' => ( $height - 35 ),
480             'class' => 'scale_minor',
481             );
482             }
483            
484             # draw blocks
485 2 50       114 if ( @blocksi ) {
486 0         0 my @y = map { $_->get_y } sort { $a->get_y <=> $b->get_y } @{ $tree->get_entities };
  0         0  
  0         0  
  0         0  
487 0         0 my $y = $y[0] - 20;
488 0         0 my $height = ( $y[-1] - $y[0] ) + 40;
489 0         0 my $width = ( $blocksi[0] - $blocksi[1] ) / 2;
490 0         0 for my $i ( @blocksi ) {
491 0         0 $self->_draw_rectangle(
492             '-x' => $i,
493             '-y' => $y,
494             '-height' => $height,
495             '-width' => $width,
496             '-fill' => 'whitesmoke',
497             '-stroke_width' => 0,
498             '-stroke' => 'whitesmoke',
499             );
500             }
501             }
502             }
503             }
504              
505             =begin comment
506              
507             Type : Internal method.
508             Title : _draw_branch
509             Usage : $svg->_draw_branch($node);
510             Function: Draws internode between $node and $node->get_parent, if any
511             Returns :
512             Args :
513              
514             =end comment
515              
516             =cut
517              
518             sub _draw_branch {
519 17     17   36 my ( $self, $node ) = @_;
520 17         82 $logger->info( "Drawing branch for " . $node->get_internal_name );
521 17 100       54 if ( my $parent = $node->get_parent ) {
522 14         59 my ( $x1, $x2 ) = ( int $parent->get_x, int $node->get_x );
523 14         65 my ( $y1, $y2 ) = ( int $parent->get_y, int $node->get_y );
524 14         45 my $shape = $self->_drawer->get_shape;
525 14         29 my $drawer = '_draw_curve';
526 14 100       67 if ( $shape =~ m/CURVY/i ) {
    50          
    0          
    0          
    0          
527 8         15 $drawer = '_draw_curve';
528             }
529             elsif ( $shape =~ m/RECT/i ) {
530 6         9 $drawer = '_draw_multi';
531             }
532             elsif ( $shape =~ m/DIAG/i ) {
533 0         0 $drawer = '_draw_line';
534             }
535             elsif ( $shape =~ m/UNROOTED/i ) {
536 0         0 $drawer = '_draw_line';
537             }
538             elsif ( $shape =~ m/RADIAL/i ) {
539 0         0 return $self->_draw_radial_branch($node);
540             }
541 14         38 return $self->$drawer(
542             '-x1' => $x1,
543             '-y1' => $y1,
544             '-x2' => $x2,
545             '-y2' => $y2,
546             '-width' => $self->_drawer->get_branch_width($node),
547             '-color' => $node->get_branch_color
548             );
549             }
550             }
551              
552             =begin comment
553              
554             Type : Internal method.
555             Title : _draw_radial_branch
556             Usage : $svg->_draw_radial_branch($node);
557             Function: Draws radial internode between $node and $node->get_parent, if any
558             Returns :
559             Args :
560              
561             =end comment
562              
563             =cut
564              
565             sub _draw_radial_branch {
566 0     0     my ( $self, $node ) = @_;
567            
568 0 0         if ( my $parent = $node->get_parent ) {
569 0           my $td = $self->_drawer;
570 0           my $center_x = $td->get_width / 2;
571 0           my $center_y = $td->get_height / 2;
572 0           my $width = $td->get_branch_width($node);
573            
574             # first the straight piece up to the arc
575 0           my ( $x1, $y1 ) = ( $node->get_x, $node->get_y );
576 0           my $rotation = $node->get_rotation;
577 0           my $parent_radius = $parent->get_generic('radius');
578 0           my ( $x2, $y2 ) = $td->polar_to_cartesian( $parent_radius, $rotation );
579 0           $x2 += $center_x;
580 0           $y2 += $center_y;
581 0           $self->_draw_line(
582             '-x1' => $x1,
583             '-y1' => $y1,
584             '-x2' => $x2,
585             '-y2' => $y2,
586             '-width' => $width,
587             '-color' => $node->get_branch_color,
588             '-linecap' => 'square'
589             );
590            
591             # then the arc
592 0           my ( $x3, $y3 ) = ( $parent->get_x, $parent->get_y );
593 0 0         if ( $parent->get_rotation < $rotation ) {
594 0           ( $x2, $x3 ) = ( $x3, $x2 );
595 0           ( $y2, $y3 ) = ( $y3, $y2 );
596             }
597             $self->_draw_arc(
598 0           '-x1' => $x2,
599             '-y1' => $y2,
600             '-x2' => $x3,
601             '-y2' => $y3,
602             '-radius' => $parent_radius,
603             '-width' => $width,
604             '-color' => $node->get_branch_color,
605             '-linecap' => 'square'
606             )
607             }
608             }
609              
610             sub _font_path {
611 0     0     my $self = shift;
612 0   0       my $font = shift || $DEFAULT_FONT;
613 0 0         if ( $^O =~ /darwin/ ) {
    0          
    0          
614 0           push @FONT_DIR, '/System/Library/Fonts', '/Library/Fonts';
615             }
616             elsif ( $^O =~ /linux/ ) {
617 0           push @FONT_DIR, '/usr/share/fonts';
618             }
619             elsif ( $^O =~ /MSWin/ ) {
620 0           push @FONT_DIR, $ENV{'WINDIR'} . '\Fonts';
621             }
622             else {
623 0           $logger->warn("Don't know where fonts are on $^O");
624             }
625 0           for my $dir ( @FONT_DIR ) {
626 0 0         if ( -e "${dir}/${font}.ttf" ) {
627 0           return "${dir}/${font}.ttf";
628             }
629             }
630 0           $logger->warn("Couldn't find font $font");
631             }
632              
633             =head1 SEE ALSO
634              
635             There is a mailing list at L
636             for any user or developer questions and discussions.
637              
638             =over
639              
640             =item L
641              
642             Treedrawer subclasses are called by the L object. Look
643             there to learn how to create tree drawings.
644              
645             =item L
646              
647             Also see the manual: L and L.
648              
649             =back
650              
651             =head1 CITATION
652              
653             If you use Bio::Phylo in published research, please cite it:
654              
655             B, B, B, B
656             and B, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
657             I B<12>:63.
658             L
659              
660             =cut
661              
662             1;