1 package CXGN
::Cview
::Chromosome
::Vector
;
5 CXGN::Cview::Chromosome::Vector - a class to draw circular vectors, plasmids, or genomes
9 This class inherits from L<CXGN::Cview::Chromosome>, but represents a circular DNA molecule. The only marker type that is supported is L<CXGN::Cview::Marker::VectorFeature>.
11 The height property of the chromosome object is used to set the height in pixels of the circular structure (represents the diameter) whereas the width property represents the thickness of the molecule. The outer edge of the vector image is represented by height/2, and the thickness is going inwards from there.
13 The mapunits2pixels function has been overridden to yield two coordinates, representing the position on the outer edge of the visual representation of the vector.
15 A function, angle(), has been added to calculate the angle for a given map position.
17 The default map units are bp (set in the constructor).
21 Lukas Mueller <lam87@cornell.edu>
28 use CXGN
::Cview
::Marker
::VectorFeature
;
30 use base qw
| CXGN
::Cview
::Chromosome
|;
48 my $self = $class->SUPER::new
(@_);
49 $self->set_units("bp");
55 =head2 mapunits2pixels
70 my $angle = $self->angle($mapunits);
72 my $x = sin($angle) * $self->get_radius() + $self->get_X();
73 my $y = cos($angle) * $self->get_radius() + $self->get_Y();
81 Usage: $v->angle($offset);
82 Desc: returns the angle on the circle for the given
84 Ret: an angle in radians
85 Args: the offset in map units
93 return (2 * $pi - ($mapunits / $self->get_length()) * ( 2 * $pi) - $pi);
114 $self->draw_chromosome($image);
115 $self->draw_markers($image);
116 $self->draw_caption($image);
122 Desc: lays out the vector and labels
125 Side Effects: coordinates calculated in layout are
126 used to render the vector with render()
134 my @right_markers = ();
135 my @left_markers = ();
137 # set each marker to the corresponding x height
138 # of its corresponding vector position
141 my $r = $self->get_radius() * 1.4;
143 foreach my $m ($self->get_markers()) {
145 my ($x, $y) = $m->get_chromosome()->mapunits2pixels($m->get_offset());
146 my $angle = $m->get_chromosome()->angle($m->get_offset());
147 $m->get_label()->set_Y($y);
149 $m->get_label()->set_reference_point($x, $y);
150 $m->set_marker_name($marker_name);
152 # is the marker on the right side of the vector?
154 if ($x >= $self->get_X()) {
155 $m->set_label_side("right");
156 $m->get_label()->set_X( $self->get_X() + $r * sin($angle) );
157 push @right_markers, $m;
160 # the label is on the left...
162 $m->get_label()->align_right(); # the line of the label should attach on the right side of the label
163 $m->get_label()->set_X( $self->get_X() + $r * sin($angle));
164 $m->set_label_side("left");
165 push @left_markers, $m;
170 @left_markers = sort _sort_by_y_coord
@left_markers;
171 @right_markers = sort _sort_by_y_coord
@right_markers;
173 @
{$self->{left_markers
}} = @left_markers;
174 @
{$self->{right_markers
}} = @right_markers;
176 $self->_distribute_labels(@left_markers);
177 $self->_distribute_labels(@right_markers);
182 sub _sort_by_y_coord
{
183 my ($a_x, $a_y) = $a->get_chromosome()->mapunits2pixels($a->get_offset());
184 my ($b_x, $b_y) = $b->get_chromosome()->mapunits2pixels($b->get_offset());
189 sub _distribute_labels
{
195 # calculate the downwards offsets
197 my $previous_labelpos = 0;
200 #die "marker name = ". $m->get_name() ."\n";
201 #if ($m->is_visible() && $m->is_label_visible()) {
202 my $offset= $m->get_offset();
203 my ($x, $y) = $self->mapunits2pixels($offset);
204 my $angle = $self->angle($offset);
205 my $labelpos = cos($angle) * ($m->get_label_spacer() + $self->get_radius()) + $self->get_Y();
206 my $labelheight = $m -> get_label_height
();
207 #print STDERR "label height: $labelheight\n";
209 if (($labelpos-$labelheight)<$previous_labelpos) {
210 $labelpos = $previous_labelpos+$labelheight;
211 if (exists($downwards{$m->get_name()})) { print STDERR
"CATASTROPHE: Duplicate marker name ".($m->get_name())."\n"; }
212 $downwards{$m->get_name()} = $labelpos;
215 $downwards{$m->get_name()}=$labelpos;
217 $previous_labelpos = $labelpos;
221 # calculate the upwards offsets
224 my @reverse_markers = reverse(@m);
225 my $top_angle = $self->angle($reverse_markers[0]->get_offset());
226 my $toplabelpos = 99999; #sin($top_angle) * ($self->get_height()/2 + $reverse_markers[0]->get_label_spacer());
227 foreach my $m (@reverse_markers) {
228 #if ($m->is_visible() && $m->is_label_visible()) {
229 my $offset=$m->get_offset();
230 my ($x, $y) = $self->mapunits2pixels($offset);
231 my $angle = $self->angle($offset);
232 my $labelpos = cos($angle) * ( $m->get_label_spacer() + $self->get_radius()) + $self->get_Y();
233 #print STDERR "VERTICAL OFFSET = ".$self->get_vertical_offset()."\n";
234 my $labelheight= $m->get_label_height();
235 # print STDERR $m->get_name()." offset = $cM ID=".$m->get_id()."\n";
236 if (($labelpos+$labelheight)>$toplabelpos) {
237 $labelpos = $toplabelpos-$labelheight;
238 if (!$m->get_name()) { print STDERR
"CATASTROPHE: Didn't get name on marker ".$m->get_id()."\n"; }
239 if (exists($upwards{$m->get_name})) { print STDERR
"CATASTHROPHE: duplicate marker name ".$m->get_name()."\n"; }
240 $upwards{$m->get_name()} = $labelpos;
243 $upwards{$m->get_name()}=$labelpos;
245 $toplabelpos = $labelpos;
249 # load into marker objects
252 my $marker_name = $m -> get_name
();
253 # test to prevent warnings...
254 if (! $downwards{$marker_name}) { $downwards{$marker_name}=0; }
255 if (! $upwards{$marker_name}) { $upwards{$marker_name} = 0; }
257 my $pixels = int(($downwards{$marker_name}+$upwards{$marker_name})/2);
259 $m->get_label()->set_Y($pixels);
262 #print STDERR "Vertical pixels for marker ".$m->get_marker_name()." : $pixels.\n";
270 =head2 draw_chromosome
281 sub draw_chromosome
{
285 # render vector backbone
287 my $draw_color = $image->colorAllocate(0,0,0);
288 $image->arc($self->get_X(), $self->get_Y(), $self->get_height(), $self->get_height(), 0, 360, $draw_color);
290 $image->arc($self->get_X(), $self->get_Y(), $self->get_height()- 2 * $self->get_width(), $self->get_height()- 2 * $self->get_width(), 0, 360, $draw_color);
292 my $fill_color = $image->colorAllocate($self->get_color());
293 $image->fill($self->get_X()+$self->get_height()/2 - 2, $self->get_Y(), $fill_color);
295 $self->draw_caption($image);
303 # first render markers with ranges, otherwise this will mess up their highlighting.
304 # then draw the other marker ticks.
305 # then draw the labels.
307 foreach my $m (@
{$self->{right_markers
}}, @
{$self->{left_markers
}}) {
308 if ($m->has_range()) { $m->render_region($image); }
310 foreach my $m (@
{$self->{right_markers
}}, @
{$self->{left_markers
}}) {
311 $m->get_label()->render($image);
313 foreach my $m (@
{$self->{right_markers
}}, @
{$self->{left_markers
}}) {
314 if (!$m->has_range()) { $m->draw_tick($image); }
323 Usage: $v->draw_caption($image)
324 Desc: draws the caption on $image, centered in
337 my $font = GD
::Font
->Giant;
338 my $color = $image->colorAllocate(0, 0, 0);
340 my $x = $self->get_X() - $font->width() * length($self->get_caption()) / 2;
341 my $y = $self->get_Y() - $font->height();
343 $image->string($font, $x, $y, $self->get_caption(), $color);
345 $font = GD
::Font
->Small;
347 my $string = "(".$self->get_length()." bp)";
348 $x = $self->get_X() - $font->width() * length($string)/2 ;
349 $y = $self->get_Y() + $font->height()/2;
350 $image->string($font, $x, $y, $string, $color);
356 Usage: my $r = $v->get_radius();
357 Desc: returns the radius of the circle deliniating the
358 vector. There is no setter, use set_height() instead,
359 which corresponds to the diameter.
369 return $self->get_height()/2;