3 CXGN::Phylo::Layout - calculates the layout of a tree.
7 The Layout class calculates the layout of a tree.
8 The layout object is then passed to a Renderer object, which
11 These classes are used by the tree object L<CXGN::Phylo::Tree> to layout the tree.
13 The layout can be subclassed to achieve different tree layouts, such as in the examples below (rendering the tree from right to left instead of left to right, etc. The CXGN::Phylo::Tree object can then be made to use the alternate layout classes by using the set_layout() accessor in L<CXGN::Phylo::Tree>. The default layout for a Tree is CXGN::Phylo::Layout, which lays out the tree from left to right (the root is left and the labels are on the right).
15 This can be combined with alternate renderers (L<CXGN::Phylo::Renderer>) to achieve a large number of different tree output options. Note that it will not be possible to combine every Layout with any Renderer.
19 Lukas Mueller <lam87@cornell.edu>
23 This class implements the following methods:
27 package CXGN
::Phylo
::Layout
;
29 =head2 constructor new()
31 Usage: my $lo = CXGN::Phylo::Layout->new($tree);
32 Desc: creates a new layout object
33 Args: a CXGN::Phylo::Tree object
34 Side Effects: lays out the tree object
43 my $self = bless $args, $class;
45 # initialize object properties
47 $self->set_tree($tree);
48 $self->set_top_margin(10);
49 $self->set_right_margin(10);
50 $self->set_bottom_margin(10);
51 $self->set_left_margin(10);
52 $self->set_label_margin(0);
53 $self->set_vertical_gap(0);
54 $self->set_horizontal_scaling_factor(0);
55 $self->set_image_width(100);
56 $self->set_image_height(100);
60 =head2 accessors get_tree(), set_tree()
62 Synopsis: my $tree = $layout -> get_tree();
63 $layout -> set_tree($tree);
64 Property: the tree this layout object works on
80 =head1 Functions controlling the the size and margins
83 =head2 accessors get_image_width(), set_image_width()
85 Synopsis: $lo->set_image_width(400);
86 Property: the width of the final image in pixels
94 return $self->{image_width
};
99 $self->{image_width
}=shift;
103 =head2 accessors get_image_height(), set_image_height()
105 Synopsis: $lo->set_image_height(300);
106 Property: the height of the final image in pixels
113 sub get_image_height
{
115 return $self->{image_height
};
118 sub set_image_height
{
120 $self->{image_height
}=shift;
123 =head2 accessors get_top_margin(), set_top_margin()
125 Synopsis: $lo->set_top_margin(10);
126 Property: the margin at the top of the image
135 return $self->{top_margin
};
140 $self->{top_margin
}=shift;
143 =head2 accessors get_left_margin(), set_margin_left()
145 Synopsis: $lo->set_left_margin(20);
146 Property: the margin at the left of the image
153 sub get_left_margin
{
155 return $self->{left_margin
};
158 sub set_left_margin
{
160 $self->{left_margin
}=shift;
163 =head2 accessors get_right_margin(), set_right_margin()
165 Synopsis: $lo->set_right_margin(10);
166 Property: the right margin in pixels [integer]
171 sub get_right_margin
{
173 return $self->{right_margin
};
176 sub set_right_margin
{
178 $self->{right_margin
}=shift;
181 =head2 accessors get_bottom_margin(), set_bottom_margin()
183 Synopsis: $lo->set_bottom_margin(10);
184 Property: the bottom margin in pixels [integer]
190 sub get_bottom_margin
{
192 return $self->{bottom_margin
};
195 sub set_bottom_margin
{
197 $self->{bottom_margin
}=shift;
200 =head2 accessors get_label_margin(), set_label_margin()
202 Synopsis: $lo->set_label_margin(10)
203 Property: the margin between the label and the leaf node
210 sub get_label_margin
{
212 return $self->{label_margin
};
215 sub set_label_margin
{
217 $self->{label_margin
}=shift;
218 # print STDERR "in set_label_margin. label margin set to: [", $self->get_label_margin(), "]\n";
222 =head1 Functions and properties used by the layout() function.
224 These should probably not be called directly, unless this class is sub-classed and layout() overridden.
226 =head2 accessors get_vertical_gap(), set_vertical_gap()
228 Synopsis: my $vertical_gap = $lo->get_vertical_gap();
229 Property: the vertical gap between leaf nodes.
235 sub get_vertical_gap
{
237 return $self->{vertical_gap
};
240 sub set_vertical_gap
{
242 $self->{vertical_gap
}=shift;
245 =head2 accessor get_horizontal_scaling_factor(), set_horizontal_scaling_factor()
247 Synopsis: $lo->set_horizontal_scaling_factor(2);
248 Property: the horizontal scaling factor [real]
250 Description: determines how the tree will be scaled horizontally,
251 in a conversion from branch length units to pixels.
255 sub get_horizontal_scaling_factor
{
257 return $self->{horizontal_scaling_factor
};
260 sub set_horizontal_scaling_factor
{
262 $self->{horizontal_scaling_factor
}=shift;
265 =head2 function layout()
268 Desc: does the layout calculation
279 # if labels are shown, calculate the longest label
280 my $longest_label = 0; # in pixels
281 foreach my $n ($self->get_tree()->get_leaf_list()) {
282 my $label = $n->get_label()->get_shown_name();
283 next if($n->get_hidden or !$n->is_leaf() or $n->get_hide_label()); # in these cases the label will not be shown, so don't consider its length.
284 if (length($label) > $longest_label) {
285 # print STDERR "in layout. new longest label: ", $label, "\n";
286 $longest_label= length($label);
289 #print STDERR "in layout. [", $longest_label, "][", $self->get_tree()->get_renderer()->get_font_width(), "]\n";
290 $self->set_label_margin($longest_label*$self->get_tree()->get_renderer()->get_font_width());
292 # perform some tree calculations
293 # -- get the distances from root, used for horizontal layout
294 my $bltype = "branch_length";
295 #$bltype = "square_root";
296 #$bltype = "proportion_different";
298 $self->get_tree()->shown_branch_length_transformation_reset($bltype); # branch_length");
299 # $self->get_tree()->get_root()->calculate_distances_from_root(0.0, $bltype);
301 # -- get the leaf list, used for vertical layout
302 $self->get_tree()->get_leaf_list();
304 $self->_layout_horizontal();
305 $self->_layout_vertical();
308 =head2 function _layout_horizontal()
310 Usage: $lo->_layout_horizontal()
311 Desc: helper function for layout, which lays out the tree
312 in the horizontal dimension
315 Side Effects: sets layout coordinates in the node objects
320 sub _layout_horizontal
{
323 # calculate the distance of each node to the root
324 # $self->get_tree()->get_root()->calculate_distances_from_root();
325 my $longest = $self->get_tree()->calculate_longest_root_leaf_length();
327 #print STDERR "AAAAA in _layout_horizontal. longest: $longest , image width: ", $self->get_image_width(), "\n";
328 $longest = 1.0 if($longest<=0);
329 $self->get_tree()->set_longest_root_leaf_length($longest);
330 # $self->set_horizontal_scaling_factor($self->get_image_width()/$largest); # is this used??? yes, in renderer
331 $self->set_horizontal_scaling_factor($self->get_image_width()/$longest); # is this used??? yes, in renderer
333 # calculate all the horizontal coordinates for each node recursively
334 $self->recursive_horizontal_coords($self->get_tree()->get_root());
337 =head2 function _layout_vertical()
339 Synopsis: helper function to layout() that lays out the vertical
340 dimension of the tree
343 Side effects: sets all the node coordinates, starting from the leaf nodes.
344 The leaf nodes are spread evenly across the image height, all
345 other node positions are calculated by recursing through the
346 tree and taking the average of the vertical coordinates of the
347 corresponding children nodes. Takes into account the margins as
348 set by set_top_margin() and set_bottom_margin().
353 sub _layout_vertical
{
356 # get the total leaf count
357 my $total_leaves = $self->get_tree()->get_leaf_count();
358 my $image_height = $self->get_image_height();
359 if ($total_leaves < 1) { return; }
360 my $vertical_gap = ($image_height - $self->get_top_margin() - $self->get_bottom_margin()) / ($total_leaves);
362 # the leaf nodes should be easy.
363 # traverse the tree, find all the leaves, and set the vertical
364 # coordinate in the leaves to an increasing value with step $vertical_gap.
365 my @leaves = $self->get_tree()->get_leaf_list();
366 for (my $i=0; $i<@leaves; $i++) {
367 $leaves[$i]->set_Y($self->get_top_margin()+($i)*$vertical_gap);
370 $self->set_vertical_gap($vertical_gap);
371 # for the remaining nodes, we just take the average of the coords for the
372 # first and the last child.
373 $self->_recursive_vertical_coords($self->get_tree()->get_root());
377 sub _recursive_vertical_coords
{
381 if ($node->is_leaf() || $node->get_hidden()) { return $node->get_vertical_coord(); }
383 # calculate average over all children
384 # if not is not hidden.
386 my @children = $node->get_children();
388 foreach my $c (@children) {
389 $total += $self->_recursive_vertical_coords($c);
392 $coord = $total/@children;
394 $node -> set_Y
($coord);
396 # print STDERR "CHILD: ".$node->get_name()." COORD: ".$node->get_vertical_coord()."\n";
401 sub recursive_horizontal_coords
{
405 # if the tree is undefined or all the branch lengths are zero, or
406 # the root node is hidden, we should not be doing this...
408 #print STDERR "in recursive_horizontal_coords. [", $self->get_tree()->get_longest_root_leaf_length(), "]\n";
409 if ($self->get_tree()->get_longest_root_leaf_length() == 0) { return; }
411 my @children = $node->get_children();
413 # if the node is hidden, don't proceed with the children! (all sub-children
414 # will be hidden too!)
416 if (!$node->get_hidden()) {
417 foreach my $c (@children) {
418 $self->recursive_horizontal_coords($c);
421 # print "dist from root, longest dist from root: " , $node->get_dist_from_root(), " ", $self->get_tree()->get_longest_root_leaf_length(), "\n";
422 my $normalized_dist_from_root = $node->get_dist_from_root()/($self->get_tree()->get_longest_root_leaf_length()); #between 0 and 1.
424 #print STDERR "in rec.horiz.coords. [", $self->get_image_width(), "][", $self->get_left_margin(), "][",
425 #$self->get_right_margin(), "][", $self->get_label_margin(), "]\n";
426 my $available_width = ($self->get_image_width()-$self->get_left_margin()-$self->get_right_margin()-$self->get_label_margin());
427 #print STDERR "in rec.horiz.coords. [", $normalized_dist_from_root, "][", $available_width, "][", $self->get_left_margin(), "]\n";
428 $node->set_X($self->get_left_margin()+$normalized_dist_from_root*$available_width);
429 # print STDERR "HORIZONTAL COORD: ".$node->get_X()."\n";
433 =head1 Package CXGN::Phylo::Layout_left_to_right;
435 This is just a subclass of Layout which does not perform any additional calculations.
439 package CXGN
::Phylo
::Layout_left_to_right
;
441 use base qw
/ CXGN::Phylo::Layout /;
444 =head1 Package CXGN::Phylo::Layout_right_to_left
446 Subclass of Layout. It performs the same calculations as layout, and then transforms
447 the resulting coordinates such that the tree will be displayed in a flipped orientation.
451 package CXGN
::Phylo
::Layout_right_to_left
;
453 use base qw
/ CXGN::Phylo::Layout /;
457 my $self = $class->SUPER::new
(@_);
463 $self->SUPER::layout
(@_);
464 $self->flip_coordinates_horizontally($self->get_tree()->get_root());
468 sub flip_coordinates_horizontally
{
471 my ($x, $y) = ($node->get_X(), $node->get_Y());
472 $node->set_X($self->get_image_width()-$x);
474 $node->get_label()->align_left();
475 foreach my $c ($node->get_children()) {
476 $self->flip_coordinates_horizontally($c);
481 =head1 Package CXGN::Phylo::Layout_top_to_bottom
483 Subclass of Layout. It performs the same calculations as layout, and then transforms
484 the resulting coordinates such that the tree will be displayed in a flipped orientation.
488 package CXGN
::Phylo
::Layout_top_to_bottom
;
490 use base qw
/ CXGN::Phylo::Layout /;
494 my $self = $class->SUPER::new
(@_);
500 my ($width, $height) = ($self->get_image_width(),$self->get_image_height());
501 $self->set_image_width($height);
502 $self->set_image_height($width);
503 $self->SUPER::layout
(@_);
504 $self->flip_coordinates_90_degrees($self->get_tree()->get_root());
505 $self->set_image_width($width);
506 $self->set_image_height($height);
510 sub flip_coordinates_90_degrees
{
513 my ($x, $y) = ($node->get_X(), $node->get_Y());
516 $node->get_label()->align_right();
517 foreach my $c ($node->get_children()) {
518 $self->flip_coordinates_90_degrees($c);
525 =head1 Package CXGN::Phylo::Layout_bottom_to_top
527 Subclass of Layout. It performs the same calculations as layout, and then transforms
528 the resulting coordinates such that the tree will be displayed in a flipped orientation.
532 package CXGN
::Phylo
::Layout_bottom_to_top
;