1package Bio::Graphics::DrawTransmembrane;
2
3use strict;
4use warnings;
5use GD;
6use base 'Bio::Root::Root';
7
8my %DRAWOPTIONS = (
9    ## general parameters
10    'topology'                => { 'private' => 'topology_array',
11                                   'default' => []},
12    'topology_string'         => { 'private' => 'topology_string',
13                                   'default' => 0},
14    'n_terminal'              => { 'private' => 'n_term',
15                                   'default' => 'out', },
16    'title'                   => { 'private' => 'title',
17                                   'default' => ''},
18    'inside_label'            => { 'private' => 'in',
19                                   'default' => "Cytoplasmic"},
20    'outside_label'           => { 'private' => 'out',
21                                   'default' => "Extracellular"},
22    'membrane_label'          => { 'private' => 'membrane',
23                                   'default' => "Plasma Membrane"},
24    ## dimensions
25    'helix_height'            => { 'private' => 'helix_height',
26                                   'default' => 130},
27    'helix_width'             => { 'private' => 'helix_width',
28                                   'default' => 50},
29    'loop_width'              => { 'private' => 'loop_width',
30                                   'default' => 20},
31    'vertical_padding'        => { 'private' => 'vertical_padding',
32                                   'default' => 140},
33    'horizontal_padding'      => { 'private' => 'horizontal_padding',
34                                   'default' => 150},
35    'membrane_offset'         => { 'private' => 'offset',
36                                   'default' => 6},
37    ## loop lengths and limits
38    'short_loop_height'       => { 'private' => 'short_loop',
39                                   'default' => 90},
40    'medium_loop_height'      => { 'private' => 'medium_loop',
41                                   'default' => 120},
42    'long_loop_height'        => { 'private' => 'long_loop',
43                                   'default' => 150},
44    'short_loop_limit'        => { 'private' => 'short_loop_limit',
45                                   'default' => 15},
46    'long_loop_limit'         => { 'private' => 'long_loop_limit',
47                                   'default' => 30},
48    'n_terminal_height'       => { 'private' => 'n_terminal_height',
49                                   'default' => 150},
50    'c_terminal_height'       => { 'private' => 'c_terminal_height',
51                                   'default' => 80},
52    'loop_heights'            => { 'private' => 'loop_heights',
53                                   'default' => {}},
54    'n_terminal_offset'       => { 'private' => 'n_term_offset',
55                                   'default' => 0},
56    'c_terminal_offset'       => { 'private' => 'c_term_offset',
57                                   'default' => 0},
58    ## colour scheme & display options
59    'show_labels'             => { 'private' => 'labels',
60                                   'default' => 'on'},
61    'bold_helices'            => { 'private' => 'bold_helices',
62                                   'default' => 1},
63    'bold_labels'             => { 'private' => 'bold_labels',
64                                   'default' => 0},
65    'colour_scheme'           => { 'private' => 'scheme',
66                                   'default' => 'yellow'},
67    'draw_cytosol'            => { 'private' => 'draw_cytosol',
68                                   'default' => 0},
69    'draw_bilayer'            => { 'private' => 'draw_bilayer',
70                                   'default' => 1},
71    'draw_loops'              => { 'private' => 'draw_loops',
72                                   'default' => 1},
73    'draw_terminai'           => { 'private' => 'draw_terminai',
74                                   'default' => 1},
75    'draw_helices'            => { 'private' => 'draw_helices',
76                                   'default' => 1},
77    ## labeling options
78    'labels'                  => { 'private' => 'loop_labels',
79                                   'default' => {}},
80    'text_offset'             => { 'private' => 'text_offset',
81                                   'default' => 0},
82    'helix_label'             => { 'private' => 'helix_label',
83                                   'default' => 'S'},
84	'n_term_label'            => { 'private' => 'n_term_label',
85								   'default' => 'N-Terminal'},
86	'c_term_label'            => { 'private' => 'c_term_label',
87								   'default' => 'C-Terminal'},
88	'dontsort'                => { 'private' => 'dontsort',
89								   'default' => 0},
90	'ttf_font'                => { 'private' => 'ttf_font',
91								   'default' => 0},
92	'ttf_font_size'           => { 'private' => 'ttf_font_size',
93								   'default' => 8},
94);
95
96sub new {
97	my ($class, @args) = @_;
98	my $self = $class->SUPER::new(@args);
99	my %opt = @args;
100	%opt = map {my $k = $_;
101		$k =~ s{^-}{};
102		$k => $opt{$_}} keys %opt;
103
104    # need to shore up private variables, check for req'd parameters
105	for my $param (sort keys %DRAWOPTIONS) {
106		my ($priv, $def) = ($DRAWOPTIONS{$param}->{'private'},$DRAWOPTIONS{$param}->{'default'});
107		$self->{$priv} = (exists $opt{$param}) ? $opt{$param} : $def;
108	}
109
110	$self->{'loop_count'} = 1;
111
112	return $self;
113}
114
115sub png {
116
117 	my $self = shift;
118
119	my @numeric = ('helix_height','helix_width','loop_width','vertical_padding','horizontal_padding','short_length','medium_loop_length','long_loop_length','short_loop_limit','long_loop_limit','n_terminal_height','membrane_offset','text_offset','n_term_offset','c_term_offset');
120
121	foreach (@numeric){
122		die "\nParameter $_ must be numeric.\n\n" if exists $self->{$_} && $self->{$_} =~ /-{?}\D+/;
123	}
124
125	foreach (keys %{$self->{'loop_labels'}}){
126		die "\nLabel position $_ must be numeric.\n\n" if $_ =~ /\D+/;
127	}
128
129	foreach (keys %{$self->{'loop_heights'}}){
130		die "\nLoop number $_ must be numeric.\n\n" if $_ =~ /\D+/;
131	}
132
133	foreach (values %{$self->{'loop_heights'}}){
134		die "\nLoop height $_ must be numeric.\n\n" if $_ =~ /\D+/;
135	}
136
137	## n-terminal defaults to outside in it's not in,inside,out,outside
138	$self->{'n_term'} = 'out' if (($self->{'n_term'} ne 'in')&&($self->{'n_term'} ne 'inside')&&($self->{'n_term'} ne 'out')||$self->{'n_term'} eq 'outside');
139	$self->{'n_term'} = 'in' if $self->{'n_term'} eq 'inside';
140
141	if ($self->{'topology_string'}){
142		$self->{'topology_string'} =~ s/\D\.//g;
143		$self->{'topology_string'} =~ s/;/,/g;
144		@{$self->{'topology_array'}} = split(/,/,$self->{'topology_string'});
145	}
146
147	## check to make sure we have pairs of helix boundaries and that data is numeric otherwise quit
148	if (scalar @{$self->{'topology_array'}} % 2){
149		die "\nUneven number of helix boundaries.\n\n";
150	}
151
152	foreach (@{$self->{'topology_array'}}){
153		if ($_ =~ /\D/){
154			die "\nTopology data is not numeric. $_\n\n";
155
156		}
157	}
158
159	## check to make sure the TTF font exists, otherwise use gdSmallFont
160	if ($self->{'ttf_font'}){
161		unless (-e $self->{'ttf_font'}){
162			print "\nCan't find font ".$self->{'ttf_font'}.".\n";
163			$self->{'ttf_font'} = 0;
164		}
165	}
166
167	my @sorted_topology = sort {$a <=> $b} @{$self->{'topology_array'}};
168
169	## Don't automatically sort the topology array
170	@sorted_topology = @{$self->{'topology_array'}} if $self->{'dontsort'};
171
172	$self->{'helix_count'} = scalar @{$self->{'topology_array'}} / 2;
173
174	unless ($self->{'helix_count'}){
175		die "\nNo topology data found.\n\n";
176	}
177
178	## put helix start/stop points in $self->{'helix_span'} and loop lengths in $self->{'loop_length'}
179	foreach (0..($self->{'helix_count'} - 1)){
180		my $count = $_ * 2;
181		$self->{'helix_span'}{$_ + 1}{'start'} = $sorted_topology[$count];
182		$self->{'helix_span'}{$_ + 1}{'stop'} = $sorted_topology[$count + 1];
183		$self->{'loop_length'}{$_ + 1} = scalar ($sorted_topology[$count + 2] - $sorted_topology[$count + 1]) unless ($_ + 1 == $self->{'helix_count'});
184	}
185
186	$self->{'width'} = ($self->{'horizontal_padding'} * 2) + ($self->{'helix_width'} * $self->{'helix_count'}) + ($self->{'loop_width'} * ($self->{'helix_count'} - 1));
187	$self->{'height'} = $self->{'helix_height'} + ($self->{'vertical_padding'} * 2);
188
189	## create a new image
190	$self->{'im'} = new GD::Image($self->{'width'},$self->{'height'});
191
192	$self->{'black'} = $self->{'im'}->colorAllocate(0,0,0);
193	$self->{'white'} = $self->{'im'}->colorAllocate(255,255,255);
194	$self->{'im'}->fill(0,0,$self->{'white'});
195
196	## write title
197	if ($self->{'ttf_font'}){
198		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,4,12,$self->{'title'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'title'};
199	}else{
200		$self->{'im'}->string(gdSmallFont,4,3,$self->{'title'},$self->{'black'}) if $self->{'title'};
201	}
202
203	$self->draw_cytosol if $self->{'draw_cytosol'};
204	$self->draw_bilayer if $self->{'draw_bilayer'};
205	$self->draw_loops if $self->{'draw_loops'};
206	$self->draw_terminai if $self->{'draw_terminai'};
207	$self->draw_helices if $self->{'draw_helices'};
208
209	## use GD to convert to png
210	return $self->{'im'}->GD::Image::png;
211
212}
213
214sub add_tmhmm_feat {
215
216	my $self = shift;
217	my $feat = shift;
218
219	#print Dumper $feat;
220
221	## add a helix from a tmhmm feature
222	if ($feat->{'_primary_tag'} eq 'transmembrane'){
223		push @{$self->{'topology_array'}},$feat->{'_location'}{'_start'};
224		push @{$self->{'topology_array'}},$feat->{'_location'}{'_end'};
225	}
226
227	## i've made a few changes to TmHmm.pm to include the inside/outside loops.
228	## this bit looks for the topology of the 1st residue so we can now position the n-terminal
229	if ($feat->{'_location'}{'_start'} == 1){
230		if ($feat->{'_primary_tag'} =~ /(\w+)_loop/){
231			$self->{'n_term'} = $1;
232		}
233	}
234
235	return $self;
236
237}
238
239sub draw_cytosol {
240
241  	my $self = shift;
242
243	my $cytosol_offset = 5;
244	my $light_grey = $self->{'im'}->colorAllocate(164,164,164);
245
246	## draw cytosol
247	$self->{'im'}->filledRectangle(($self->{'horizontal_padding'} / 3) ,($self->{'vertical_padding'} + $self->{'helix_height'} - $cytosol_offset),($self->{'width'} - ($self->{'horizontal_padding'} / 3)),($self->{'vertical_padding'} + ($self->{'helix_height'} * 2 ) + $cytosol_offset),$light_grey);
248
249	return $self;
250}
251
252sub draw_bilayer {
253
254  	my $self = shift;
255
256	my $dark_grey = $self->{'im'}->colorAllocate(40,40,40);
257	my $dark_grey1 = $self->{'im'}->colorAllocate(50,50,50);
258	my $dark_grey2 = $self->{'im'}->colorAllocate(60,60,60);
259	my $dark_grey3 = $self->{'im'}->colorAllocate(70,70,70);
260
261	## label either side of membrane
262	if ($self->{'ttf_font'}){
263
264		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} / 3) + 2,($self->{'vertical_padding'} + $self->{'offset'} - 3),$self->{'out'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
265		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} / 3) + 2,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'} + 12),$self->{'in'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
266	}else{
267		$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} / 3) + 2,($self->{'vertical_padding'} + $self->{'offset'} - 14),$self->{'out'},$self->{'black'}) if $self->{'labels'};
268		$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} / 3) + 2,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'} + 1),$self->{'in'},$self->{'black'}) if $self->{'labels'};
269	}
270
271
272
273	## draw membrane with graded fill
274	$self->{'im'}->filledRectangle(($self->{'horizontal_padding'} / 3),($self->{'vertical_padding'} + $self->{'offset'}),($self->{'width'} - $self->{'horizontal_padding'} / 3),($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'}),$dark_grey);
275	$self->{'im'}->filledRectangle(($self->{'horizontal_padding'} / 3) + 1,($self->{'vertical_padding'} + $self->{'offset'}) + 1,($self->{'width'} - $self->{'horizontal_padding'} / 3) - 1,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'}) - 1,$dark_grey1);
276	$self->{'im'}->filledRectangle(($self->{'horizontal_padding'} / 3) + 2,($self->{'vertical_padding'} + $self->{'offset'}) + 2,($self->{'width'} - $self->{'horizontal_padding'} / 3) - 2,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'}) - 2,$dark_grey2);
277	$self->{'im'}->filledRectangle(($self->{'horizontal_padding'} / 3) + 3,($self->{'vertical_padding'} + $self->{'offset'}) + 3,($self->{'width'} - $self->{'horizontal_padding'} / 3) - 3,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'}) - 3,$dark_grey3);
278
279	## label membrane
280	if ($self->{'ttf_font'}){
281		$self->{'im'}->stringFT($self->{'white'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} + ($self->{'helix_count'} * $self->{'helix_width'}) + (($self->{'helix_count'} - 1) * $self->{'loop_width'}) + 4) + 1,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'} - 3),$self->{'membrane'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
282	}else{
283		$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} + ($self->{'helix_count'} * $self->{'helix_width'}) + (($self->{'helix_count'} - 1) * $self->{'loop_width'}) + 4) + 1,($self->{'vertical_padding'} + $self->{'helix_height'} - $self->{'offset'} - 14),$self->{'membrane'},$self->{'white'}) if $self->{'labels'};
284	}
285
286
287	return $self;
288
289}
290
291sub draw_helices {
292
293	my $self = shift;
294
295	my $x = $self->{'horizontal_padding'};
296	my $y = $self->{'vertical_padding'};
297
298	my ($colour,$colour1,$colour2,$colour3,$colour4,$colour5,$colour6);
299
300	if($self->{'scheme'} eq 'blue'){
301
302		$colour = $self->{'im'}->colorAllocate(90,160,255);
303		$colour1 = $self->{'im'}->colorAllocate(80,150,255);
304		$colour2 = $self->{'im'}->colorAllocate(70,140,255);
305		$colour3 = $self->{'im'}->colorAllocate(60,130,255);
306		$colour4 = $self->{'im'}->colorAllocate(50,120,255);
307		$colour5 = $self->{'im'}->colorAllocate(40,110,255);
308		$colour6 = $self->{'im'}->colorAllocate(30,100,255);
309
310	}elsif($self->{'scheme'} eq 'pink'){
311
312		$colour = $self->{'im'}->colorAllocate(255,1,255);
313		$colour1 = $self->{'im'}->colorAllocate(240,1,255);
314		$colour2 = $self->{'im'}->colorAllocate(230,1,255);
315		$colour3 = $self->{'im'}->colorAllocate(220,1,255);
316		$colour4 = $self->{'im'}->colorAllocate(200,1,255);
317		$colour5 = $self->{'im'}->colorAllocate(180,1,255);
318		$colour6 = $self->{'im'}->colorAllocate(160,1,255);
319
320	}elsif($self->{'scheme'} eq 'green'){
321
322		$colour = $self->{'im'}->colorAllocate(5,240,0);
323		$colour1 = $self->{'im'}->colorAllocate(5,230,0);
324		$colour2 = $self->{'im'}->colorAllocate(5,220,0);
325		$colour3 = $self->{'im'}->colorAllocate(5,205,0);
326		$colour4 = $self->{'im'}->colorAllocate(5,195,0);
327		$colour5 = $self->{'im'}->colorAllocate(5,185,0);
328		$colour6 = $self->{'im'}->colorAllocate(5,155,0);
329
330	}elsif($self->{'scheme'} eq 'red'){
331
332		$colour = $self->{'im'}->colorAllocate(240,0,0);
333		$colour1 = $self->{'im'}->colorAllocate(230,0,0);
334		$colour2 = $self->{'im'}->colorAllocate(220,0,0);
335		$colour3 = $self->{'im'}->colorAllocate(205,0,0);
336		$colour4 = $self->{'im'}->colorAllocate(190,0,0);
337		$colour5 = $self->{'im'}->colorAllocate(170,0,0);
338		$colour6 = $self->{'im'}->colorAllocate(150,0,0);
339
340	}elsif($self->{'scheme'} eq 'white'){
341
342		$colour = $self->{'white'};
343		$colour1 = $self->{'white'};
344		$colour2 = $self->{'white'};
345		$colour3 = $self->{'white'};
346		$colour4 = $self->{'white'};
347		$colour5 = $self->{'black'};
348		$colour6 = $self->{'black'};
349
350	}else{
351
352		## default is yellow
353
354		$colour = $self->{'im'}->colorAllocate(255,235,55);
355		$colour1 = $self->{'im'}->colorAllocate(255,230,50);
356		$colour2 = $self->{'im'}->colorAllocate(255,220,40);
357		$colour3 = $self->{'im'}->colorAllocate(255,210,30);
358		$colour4 = $self->{'im'}->colorAllocate(255,200,20);
359		$colour5 = $self->{'im'}->colorAllocate(255,190,10);
360		$colour6 = $self->{'im'}->colorAllocate(255,180,0);
361	}
362
363
364	for (1..$self->{'helix_count'}){
365
366		## draw helix, with graduated fill
367		$self->{'im'}->filledRectangle($x,$y,($x + $self->{'helix_width'})-1,($y + $self->{'helix_height'})-1,$colour6);
368		$self->{'im'}->filledRectangle($x+1,$y+1,($x + $self->{'helix_width'})-1,($y + $self->{'helix_height'})-1,$colour5);
369		$self->{'im'}->filledRectangle($x+2,$y+2,($x + $self->{'helix_width'})-2,($y + $self->{'helix_height'})-2,$colour4);
370		$self->{'im'}->filledRectangle($x+3,$y+3,($x + $self->{'helix_width'})-3,($y + $self->{'helix_height'})-3,$colour3);
371		$self->{'im'}->filledRectangle($x+4,$y+4,($x + $self->{'helix_width'})-4,($y + $self->{'helix_height'})-4,$colour2);
372		$self->{'im'}->filledRectangle($x+5,$y+5,($x + $self->{'helix_width'})-5,($y + $self->{'helix_height'})-5,$colour1);
373		$self->{'im'}->filledRectangle($x+6,$y+6,($x + $self->{'helix_width'})-6,($y + $self->{'helix_height'})-6,$colour);
374
375		## draw a white box around it
376		if ($self->{'bold_helices'}){
377			$self->{'im'}->rectangle($x,$y,($x + $self->{'helix_width'}),($y + $self->{'helix_height'}),$self->{'black'});
378			$self->{'im'}->rectangle($x - 1,$y - 1,($x + $self->{'helix_width'} + 1),($y + $self->{'helix_height'} + 1),$self->{'white'});
379		}else{
380			$self->{'im'}->rectangle($x,$y,($x + $self->{'helix_width'}),($y + $self->{'helix_height'}),$self->{'white'});
381		}
382
383		## this is the text on each helix
384		my $text = substr($self->{'helix_label'},0,1).$_;
385
386		## draw a white box in the centre and label the helix
387		my $x_offset = 5;
388		$x_offset = 8 if $_ >= 10;
389
390		my $white_box = 12;
391		$white_box = 17 if $_ >= 10;
392
393		$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2) - $x_offset) - 5,($y + ($self->{'helix_height'} / 2) - 7) - 3,$white_box + ($x + ($self->{'helix_width'} / 2) - $x_offset) + 3,12 + ($y + ($self->{'helix_height'} / 2) - 4),$colour1) if $self->{'labels'};
394		$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2) - $x_offset) - 4,($y + ($self->{'helix_height'} / 2) - 7) - 2,$white_box + ($x + ($self->{'helix_width'} / 2) - $x_offset) + 2,12 + ($y + ($self->{'helix_height'} / 2) - 5),$colour2) if $self->{'labels'};
395		$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2) - $x_offset) - 3,($y + ($self->{'helix_height'} / 2) - 7) - 1,$white_box + ($x + ($self->{'helix_width'} / 2) - $x_offset) + 1,12 + ($y + ($self->{'helix_height'} / 2) - 6),$colour3) if $self->{'labels'};
396
397
398		$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2) - $x_offset) - 2,($y + ($self->{'helix_height'} / 2) - 7),$white_box + ($x + ($self->{'helix_width'} / 2) - $x_offset),12 + ($y + ($self->{'helix_height'} / 2) - 7),$self->{'white'}) if $self->{'labels'};
399
400		if ($self->{'ttf_font'}){
401			$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($x + ($self->{'helix_width'} / 2) - $x_offset - 1),($y + ($self->{'helix_height'} / 2) + 4),$text,{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
402		}else{
403			$self->{'im'}->string(gdSmallFont,($x + ($self->{'helix_width'} / 2) - $x_offset),($y + ($self->{'helix_height'} / 2) - 7),$text,$self->{'black'}) if $self->{'labels'};
404		}
405
406		## label start and end positions of helices
407
408		$self->{'x'} = $x;
409		$self->{'y'} = $y;
410
411		if ($self->{'labels'}){
412
413			if (($self->{'n_term'} eq 'out')&&($_ % 2)){
414
415				$self->label_helix_o_i();
416
417			}elsif($self->{'n_term'} eq 'out'){
418
419				$self->label_helix_i_o();
420
421			}elsif(($self->{'n_term'} eq 'in')&&($_ % 2)){
422
423				$self->label_helix_i_o();
424
425			}else{
426
427				$self->label_helix_o_i();
428
429			}
430		}
431
432		$x = $self->{'horizontal_padding'} + ($_ * ($self->{'helix_width'} + $self->{'loop_width'}));
433
434	}
435
436	if ($self->{'labels'}){
437
438		$x = $self->{'horizontal_padding'};
439		$y = $self->{'vertical_padding'};
440
441		for (1..$self->{'helix_count'}){
442
443			my $y_mod = 0;
444
445			foreach my $l (sort {$b <=> $a} keys %{$self->{'loop_labels'}}){
446
447				if (($l >= $self->{'helix_span'}{$_}{'start'})&&($l <= $self->{'helix_span'}{$_}{'stop'})){
448
449					my $label_length = 0;
450					if ($self->{'ttf_font'}){
451						## Might need to fiddle with this
452						my $size_dif = $self->{'ttf_font_size'} - 8;
453						$label_length = 6 + (6 * (length $self->{'loop_labels'}{$l}) + (8 * $size_dif));
454					}else{
455						$label_length = 9 + (6 * length $self->{'loop_labels'}{$l});
456					}
457
458					if ($_ % 2){
459
460						if ($self->{'bold_labels'}){
461							$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2)) + 5,($y + ($self->{'helix_height'} / 2) - 30) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)) + 2,12 + ($y + ($self->{'helix_height'} / 2) - 24) + $y_mod,$self->{'black'});
462							my $b = new GD::Polygon;
463        						$b->addPt(($x + ($self->{'helix_width'} / 2)) + 4,($y + ($self->{'helix_height'} / 2) - 30) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
464        						$b->addPt(($x + ($self->{'helix_width'} / 2)) - 5,($y + ($self->{'helix_height'} / 2) - 21) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
465       							$b->addPt(($x + ($self->{'helix_width'} / 2)) + 4,($y + ($self->{'helix_height'} / 2) - 12) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
466        						$self->{'im'}->filledPolygon($b,$self->{'black'});
467						}
468
469						## add darker box
470						$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2)) + 6,($y + ($self->{'helix_height'} / 2) - 29) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)) + 1,12 + ($y + ($self->{'helix_height'} / 2) - 25) + $y_mod,$colour6);
471						## add white box
472						$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2)) + 7,($y + ($self->{'helix_height'} / 2) - 28) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)),12 + ($y + ($self->{'helix_height'} / 2) - 26) + $y_mod,$self->{'white'});
473
474
475						## draw darker arrowhead
476				        	my $poly = new GD::Polygon;
477        					$poly->addPt(($x + ($self->{'helix_width'} / 2)) + 5,($y + ($self->{'helix_height'} / 2) - 29) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
478        					$poly->addPt(($x + ($self->{'helix_width'} / 2)) - 3,($y + ($self->{'helix_height'} / 2) - 21) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
479       						$poly->addPt(($x + ($self->{'helix_width'} / 2)) + 5,($y + ($self->{'helix_height'} / 2) - 13) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
480        					$self->{'im'}->filledPolygon($poly,$colour6);
481
482						## draw white arrowhead
483				        	my $poly2 = new GD::Polygon;
484        					$poly2->addPt(($x + ($self->{'helix_width'} / 2)) + 6,($y + ($self->{'helix_height'} / 2) - 28) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
485        					$poly2->addPt(($x + ($self->{'helix_width'} / 2)) - 1,($y + ($self->{'helix_height'} / 2) - 21) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
486       						$poly2->addPt(($x + ($self->{'helix_width'} / 2)) + 6,($y + ($self->{'helix_height'} / 2) - 14) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
487        					$self->{'im'}->filledPolygon($poly2,$self->{'white'});
488
489						## add label
490						if ($self->{'ttf_font'}){
491							$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($x + ($self->{'helix_width'} / 2)) + 10,($y + ($self->{'helix_height'} / 2) - 16) + $y_mod,$self->{'loop_labels'}{$l},{linespacing=>0.6,charmap  => 'Unicode',});
492						}else{
493							$self->{'im'}->string(gdSmallFont,($x + ($self->{'helix_width'} / 2)) + 9,($y + ($self->{'helix_height'} / 2) - 27) + $y_mod,$self->{'loop_labels'}{$l},$self->{'black'});
494						}
495
496						$y_mod = $y_mod - 19;
497
498					}else{
499
500						if ($self->{'bold_labels'}){
501							$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2)) + 5,($y + ($self->{'helix_height'} / 2) + 9) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)) + 2,13 + ($y + ($self->{'helix_height'} / 2) + 15) + $y_mod,$self->{'black'});
502				        		my $b = new GD::Polygon;
503        						$b->addPt(($x + ($self->{'helix_width'} / 2)) + 4,($y + ($self->{'helix_height'} / 2) + 10) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
504        						$b->addPt(($x + ($self->{'helix_width'} / 2)) - 5,($y + ($self->{'helix_height'} / 2) + 19) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
505       							$b->addPt(($x + ($self->{'helix_width'} / 2)) + 4,($y + ($self->{'helix_height'} / 2) + 28) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
506        						$self->{'im'}->filledPolygon($b,$self->{'black'});
507						}
508
509						## add darker box
510						$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2)) + 6,($y + ($self->{'helix_height'} / 2) + 11) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)) + 1,12 + ($y + ($self->{'helix_height'} / 2) + 15) + $y_mod,$colour6);
511
512						## add white box
513						$self->{'im'}->filledRectangle(($x + ($self->{'helix_width'} / 2)) + 7,($y + ($self->{'helix_height'} / 2) + 12) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)),12 + ($y + ($self->{'helix_height'} / 2) + 14) + $y_mod,$self->{'white'});
514
515						## draw darker arrowhead
516				        	my $poly = new GD::Polygon;
517        					$poly->addPt(($x + ($self->{'helix_width'} / 2)) + 5,($y + ($self->{'helix_height'} / 2) + 11) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
518        					$poly->addPt(($x + ($self->{'helix_width'} / 2)) - 3,($y + ($self->{'helix_height'} / 2) + 19) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
519       						$poly->addPt(($x + ($self->{'helix_width'} / 2)) + 5,($y + ($self->{'helix_height'} / 2) + 27) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
520        					$self->{'im'}->filledPolygon($poly,$colour6);
521
522						## draw white arrowhead
523				        	my $poly2 = new GD::Polygon;
524        					$poly2->addPt(($x + ($self->{'helix_width'} / 2)) + 6,($y + ($self->{'helix_height'} / 2) + 12) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
525        					$poly2->addPt(($x + ($self->{'helix_width'} / 2)) - 1,($y + ($self->{'helix_height'} / 2) + 19) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
526       						$poly2->addPt(($x + ($self->{'helix_width'} / 2)) + 6,($y + ($self->{'helix_height'} / 2) + 26) + $y_mod,$label_length + ($x + ($self->{'helix_width'} / 2)));
527        					$self->{'im'}->filledPolygon($poly2,$self->{'white'});
528
529						## add label
530						if ($self->{'ttf_font'}){
531							$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($x + ($self->{'helix_width'} / 2)) + 10,($y + ($self->{'helix_height'} / 2) + 24) + $y_mod,$self->{'loop_labels'}{$l},{linespacing=>0.6,charmap  => 'Unicode',});
532						}else{
533							$self->{'im'}->string(gdSmallFont,($x + ($self->{'helix_width'} / 2)) + 9,($y + ($self->{'helix_height'} / 2) + 12) + $y_mod,$self->{'loop_labels'}{$l},$self->{'black'});
534						}
535
536						$y_mod = $y_mod + 19;
537					}
538				}
539			}
540
541			$x = $self->{'horizontal_padding'} + ($_ * ($self->{'helix_width'} + $self->{'loop_width'}));
542		}
543	}
544
545	return $self;
546}
547
548sub draw_terminai {
549
550	my $self = shift;
551
552	my $loop_number = ($self->{'helix_count'} - 1);
553
554	## width of terminal
555	$self->{'w'} = $self->{'helix_width'} + $self->{'loop_width'};
556	$self->{'cx'} = $self->{'horizontal_padding'} - ($self->{'loop_width'} / 2);
557	$self->{'cy'} = $self->{'vertical_padding'};
558
559	## draw N-terminal
560	if ($self->{'n_term'} eq 'out'){
561
562		$self->{'im'}->arc(($self->{'cx'} - $self->{'n_term_offset'}),$self->{'cy'},($self->{'w'} + (2 * $self->{'n_term_offset'})),$self->{'n_terminal_height'},270,360,$self->{'black'});
563		if ($self->{'ttf_font'}){
564			$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 33 - $self->{'n_term_offset'}),($self->{'cy'} - ($self->{'n_terminal_height'} / 2) + 5),$self->{'n_term_label'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
565		}else{
566			$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 40 - $self->{'n_term_offset'}),($self->{'cy'} - ($self->{'n_terminal_height'} / 2) - 6),$self->{'n_term_label'},$self->{'black'}) if $self->{'labels'};
567		}
568
569
570
571		## label n-terminal
572		my $y_mod = 0;
573		foreach (sort {$b <=> $a} keys %{$self->{'loop_labels'}}){
574			if ($_ <= $self->{'helix_span'}{1}{'start'}){
575
576				if ($self->{'ttf_font'}){
577					$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 33 - $self->{'n_term_offset'}),($self->{'cy'} - ($self->{'n_terminal_height'} / 2) - 6) - 4 + $y_mod,$self->{'loop_labels'}{$_},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
578				}else{
579					$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 40 - $self->{'n_term_offset'}),($self->{'cy'} - ($self->{'n_terminal_height'} / 2) - 6) - 15 + $y_mod,$self->{'loop_labels'}{$_},$self->{'black'}) if $self->{'labels'};
580				}
581
582				$y_mod = $y_mod - 15;
583			}
584		}
585
586	}else{
587		$self->{'cy'} = $self->{'cy'} + $self->{'helix_height'};
588		$self->{'im'}->arc(($self->{'cx'} - $self->{'n_term_offset'}),$self->{'cy'},($self->{'w'} + (2 * $self->{'n_term_offset'})),$self->{'n_terminal_height'},0,90,$self->{'black'});
589
590		if ($self->{'ttf_font'}){
591			$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 33 - $self->{'n_term_offset'}),($self->{'cy'} + ($self->{'n_terminal_height'} / 2) + 5),$self->{'n_term_label'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
592		}else{
593			$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 40 - $self->{'n_term_offset'}),($self->{'cy'} + ($self->{'n_terminal_height'} / 2) - 6),$self->{'n_term_label'},$self->{'black'}) if $self->{'labels'};
594		}
595
596		## label n-terminal
597		my $y_mod = 0;
598		foreach (sort {$a <=> $b} keys %{$self->{'loop_labels'}}){
599			if ($_ <= $self->{'helix_span'}{1}{'start'}){
600
601			if ($self->{'ttf_font'}){
602				$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 33 - $self->{'n_term_offset'}),($self->{'cy'} + ($self->{'n_terminal_height'} / 2) - 6) + 26 + $y_mod,$self->{'loop_labels'}{$_},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
603			}else{
604				$self->{'im'}->string(gdSmallFont,($self->{'horizontal_padding'} - ($self->{'w'} / 2) - 40 - $self->{'n_term_offset'}),($self->{'cy'} + ($self->{'n_terminal_height'} / 2) - 6) + 15 + $y_mod,$self->{'loop_labels'}{$_},$self->{'black'}) if $self->{'labels'};
605			}
606
607				$y_mod = $y_mod + 15;
608			}
609		}
610
611	}
612
613	$self->{'cx'} = ($self->{'helix_count'} * $self->{'helix_width'}) + (($self->{'helix_count'} - 1) * $self->{'loop_width'}) + $self->{'horizontal_padding'} + ($self->{'loop_width'} / 2);
614
615	## draw C-terminal
616	if (($self->{'n_term'} eq 'out')&&($loop_number % 2)){
617
618		$self->draw_ext_c_term;
619
620	}elsif($self->{'n_term'} eq 'out'){
621
622		$self->draw_int_c_term;
623
624	}elsif(($self->{'n_term'} eq 'in')&&($loop_number % 2)){
625
626		$self->draw_int_c_term;
627
628	}elsif($self->{'n_term'} eq 'in'){
629
630		$self->draw_ext_c_term;
631
632	}
633
634	return $self;
635}
636
637sub draw_loops {
638
639	my $self = shift;
640
641	$self->{'x'} = $self->{'horizontal_padding'} + ($self->{'helix_width'} / 2);
642	$self->{'h'} = $self->{'medium_loop'};
643	$self->{'w'} = $self->{'helix_width'} + $self->{'loop_width'};
644
645	for (1..($self->{'helix_count'} - 1)){
646
647		## Alter loop height according to its actual length
648		if ($self->{'loop_length'}{$_} < $self->{'short_loop_limit'}){
649			$self->{'h'} = $self->{'short_loop'};
650		}elsif($self->{'loop_length'}{$_} > $self->{'long_loop_limit'}){
651			$self->{'h'} = $self->{'long_loop'};
652		}
653
654		$self->{'l_start'} = $self->{'helix_span'}{$_}{'stop'};
655		$self->{'l_stop'} = $self->{'helix_span'}{$_ + 1}{'start'};
656
657		if (($self->{'n_term'} eq 'out')&&($_ % 2)){
658
659			$self->draw_int_loop;
660
661		}elsif($self->{'n_term'} eq 'out'){
662			$self->draw_ext_loop;
663
664		}elsif(($self->{'n_term'} eq 'in')&&($_ % 2)){
665			$self->draw_ext_loop;
666
667		}elsif($self->{'n_term'} eq 'in'){
668
669			$self->draw_int_loop;
670
671		}
672
673		$self->{'x'} = $self->{'x'} + $self->{'helix_width'} + $self->{'loop_width'};
674		$self->{'x'} = $self->{'horizontal_padding'} if $_ == ($self->{'helix_count'} - 1);
675
676	}
677
678	return $self;
679}
680
681sub label_helix_o_i {
682
683	my $self = shift;
684
685	my $offset = 6;
686	$offset = 3 if $self->{'helix_span'}{$_}{'start'} < 100;
687	if ($self->{'ttf_font'}){
688		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,$self->{'y'} + 13,$self->{'helix_span'}{$_}{'start'},{linespacing=>0.6,charmap  => 'Unicode',});
689	}else{
690		$self->{'im'}->string(gdSmallFont,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,$self->{'y'} + 1,$self->{'helix_span'}{$_}{'start'},$self->{'black'});
691	}
692
693	$offset = 3 if $self->{'helix_span'}{$_}{'stop'} < 100;
694	$offset = 6 if $self->{'helix_span'}{$_}{'stop'} >= 100;
695
696	if ($self->{'ttf_font'}){
697		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,($self->{'y'} + $self->{'helix_height'} - 3),$self->{'helix_span'}{$_}{'stop'},{linespacing=>0.6,charmap  => 'Unicode',});
698	}else{
699		$self->{'im'}->string(gdSmallFont,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,($self->{'y'} + $self->{'helix_height'} - 14),$self->{'helix_span'}{$_}{'stop'},$self->{'black'});
700	}
701
702	return $self;
703}
704
705sub label_helix_i_o {
706
707	my $self = shift;
708
709	my $offset = 6;
710	$offset = 3 if $self->{'helix_span'}{$_}{'stop'} < 100;
711	if ($self->{'ttf_font'}){
712		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,$self->{'y'} + 13,$self->{'helix_span'}{$_}{'stop'},{linespacing=>0.6,charmap  => 'Unicode',});
713	}else{
714		$self->{'im'}->string(gdSmallFont,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,$self->{'y'} + 1,$self->{'helix_span'}{$_}{'stop'},$self->{'black'});
715	}
716
717	$offset = 3 if $self->{'helix_span'}{$_}{'start'} < 100;
718
719	if ($self->{'ttf_font'}){
720		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,($self->{'y'} + $self->{'helix_height'} - 3),$self->{'helix_span'}{$_}{'start'},{linespacing=>0.6,charmap  => 'Unicode',});
721	}else{
722		$self->{'im'}->string(gdSmallFont,($self->{'x'} + ($self->{'helix_width'} / 2) - $offset) - 1,($self->{'y'} + $self->{'helix_height'} - 14),$self->{'helix_span'}{$_}{'start'},$self->{'black'});
723	}
724
725	return $self;
726}
727
728sub draw_int_c_term {
729
730	my $self = shift;
731
732	## draw internal c-terminal
733	$self->{'im'}->arc(($self->{'cx'} + $self->{'c_term_offset'}),($self->{'vertical_padding'} + $self->{'helix_height'}),($self->{'w'} + (2 * $self->{'c_term_offset'})),$self->{'c_terminal_height'},90,180,$self->{'black'});
734
735	if ($self->{'ttf_font'}){
736		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'cx'} + 4 + $self->{'c_term_offset'}),(($self->{'vertical_padding'} + $self->{'helix_height'}) + ($self->{'c_terminal_height'} / 2) + 5),$self->{'c_term_label'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
737	}else{
738		$self->{'im'}->string(gdSmallFont,($self->{'cx'} + 3 + $self->{'c_term_offset'}),(($self->{'vertical_padding'} + $self->{'helix_height'}) + ($self->{'c_terminal_height'} / 2) - 6),$self->{'c_term_label'},$self->{'black'}) if $self->{'labels'};
739	}
740
741	## label terminal
742	if ($self->{'labels'}){
743		my $y_mod = 0;
744		foreach (sort {$a <=> $b} keys %{$self->{'loop_labels'}}){
745			if ($_ >= $self->{'helix_span'}{$self->{'helix_count'}}{'stop'}){
746
747				if ($self->{'ttf_font'}){
748					$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'cx'} + 4 + $self->{'c_term_offset'}),(($self->{'vertical_padding'} + $self->{'helix_height'}) + ($self->{'c_terminal_height'} / 2) - 6) + 26 + $y_mod,$self->{'loop_labels'}{$_},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
749				}else{
750					$self->{'im'}->string(gdSmallFont,($self->{'cx'} + 3 + $self->{'c_term_offset'}),(($self->{'vertical_padding'} + $self->{'helix_height'}) + ($self->{'c_terminal_height'} / 2) - 6) + 15 + $y_mod,$self->{'loop_labels'}{$_},$self->{'black'});
751				}
752				$y_mod = $y_mod + 15;
753			}
754		}
755	}
756	return $self;
757}
758
759sub draw_ext_c_term {
760
761	my $self = shift;
762
763	## draw external c-terminal
764	$self->{'im'}->arc(($self->{'cx'} + $self->{'c_term_offset'}),$self->{'vertical_padding'},($self->{'w'} + (2 * $self->{'c_term_offset'})),$self->{'c_terminal_height'},180,270,$self->{'black'});
765
766	if ($self->{'ttf_font'}){
767		$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,,($self->{'cx'} + 3 + $self->{'c_term_offset'}),($self->{'vertical_padding'} - ($self->{'c_terminal_height'} / 2) + 5),$self->{'c_term_label'},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
768	}else{
769		$self->{'im'}->string(gdSmallFont,($self->{'cx'} + 3 + $self->{'c_term_offset'}),($self->{'vertical_padding'} - ($self->{'c_terminal_height'} / 2) - 6),$self->{'c_term_label'},$self->{'black'}) if $self->{'labels'};
770	}
771
772
773	## label terminal
774	if ($self->{'labels'}){
775		my $y_mod = 0;
776		foreach (sort {$b <=> $a} keys %{$self->{'loop_labels'}}){
777			if ($_ >= $self->{'helix_span'}{$self->{'helix_count'}}{'stop'}){
778
779				if ($self->{'ttf_font'}){
780					$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'cx'} + 3 + $self->{'c_term_offset'}),($self->{'vertical_padding'} - ($self->{'c_terminal_height'} / 2) - 6) - 4 - $y_mod,$self->{'loop_labels'}{$_},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
781				}else{
782					$self->{'im'}->string(gdSmallFont,($self->{'cx'} + 3 + $self->{'c_term_offset'}),($self->{'vertical_padding'} - ($self->{'c_terminal_height'} / 2) - 6) - 15 - $y_mod,$self->{'loop_labels'}{$_},$self->{'black'});
783				}
784				$y_mod = $y_mod + 15;
785			}
786		}
787	}
788	return $self;
789}
790
791sub draw_int_loop {
792
793	my $self = shift;
794
795	## draw internal loop
796	$self->{'cx'} = $self->{'x'} + ($self->{'helix_width'} / 2) + ($self->{'loop_width'} / 2);
797
798	## this sets the height to the value given by the loop_heights hash
799	foreach (sort {$a <=> $b} keys %{$self->{'loop_heights'}}){
800		$self->{'h'} = $self->{'loop_heights'}{$_} if $_ == $self->{'loop_count'};
801	}
802
803	$self->{'im'}->arc($self->{'cx'},($self->{'vertical_padding'} + $self->{'helix_height'}),$self->{'w'},$self->{'h'},0,180,$self->{'black'});
804
805	## label loop
806	if ($self->{'labels'}){
807		my $y_mod = 0;
808		foreach (sort {$a <=> $b} keys %{$self->{'loop_labels'}}){
809			if (($_ >= $self->{'l_start'})&&($_ <= $self->{'l_stop'})){
810				$self->{'im'}->line($self->{'cx'},($self->{'vertical_padding'} + $self->{'helix_height'} + ($self->{'h'} / 2)),$self->{'cx'},($self->{'vertical_padding'} + $self->{'helix_height'} + ($self->{'h'} / 2) + 5),$self->{'black'}) unless $y_mod;
811
812				if ($self->{'ttf_font'}){
813					$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'cx'} + $self->{'text_offset'} - 3),($self->{'vertical_padding'} + $self->{'helix_height'} + ($self->{'h'} / 2) + 17) + $y_mod,$self->{'loop_labels'}{$_},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
814				}else{
815					$self->{'im'}->string(gdSmallFont,($self->{'cx'} + $self->{'text_offset'}),($self->{'vertical_padding'} + $self->{'helix_height'} + ($self->{'h'} / 2) + 6) + $y_mod,$self->{'loop_labels'}{$_},$self->{'black'}) ;
816				}
817				$y_mod = $y_mod + 15;
818			}
819		}
820	}
821	$self->{'loop_count'}++;
822	return $self;
823}
824
825sub draw_ext_loop {
826
827	my $self = shift;
828
829	## draw external loop
830	$self->{'cx'} = $self->{'x'} + ($self->{'helix_width'} / 2) + ($self->{'loop_width'} / 2);
831
832	## this sets the height to the value given by the loop_heights hash
833	foreach (sort {$a <=> $b} keys %{$self->{'loop_heights'}}){
834		$self->{'h'} = $self->{'loop_heights'}{$_} if $_ == $self->{'loop_count'};
835	}
836
837	$self->{'im'}->arc($self->{'cx'},$self->{'vertical_padding'},$self->{'w'},$self->{'h'},180,360,$self->{'black'});
838
839	## label loop
840	if ($self->{'labels'}){
841		my $y_mod = 0;
842		foreach (sort {$b <=> $a} keys %{$self->{'loop_labels'}}){
843			if (($_ >= $self->{'l_start'})&&($_ <= $self->{'l_stop'})){
844				$self->{'im'}->line($self->{'cx'},($self->{'vertical_padding'} - ($self->{'h'} / 2)),$self->{'cx'},($self->{'vertical_padding'} - ($self->{'h'} / 2) - 5),$self->{'black'}) unless $y_mod;
845
846				if ($self->{'ttf_font'}){
847					$self->{'im'}->stringFT($self->{'black'},$self->{'ttf_font'},$self->{'ttf_font_size'},0,($self->{'cx'} + $self->{'text_offset'} - 3),($self->{'vertical_padding'} - ($self->{'h'} / 2) - 8) + $y_mod,$self->{'loop_labels'}{$_},{linespacing=>0.6,charmap  => 'Unicode',}) if $self->{'labels'};
848				}else{
849					$self->{'im'}->string(gdSmallFont,($self->{'cx'} + $self->{'text_offset'}),($self->{'vertical_padding'} - ($self->{'h'} / 2) - 19) + $y_mod,$self->{'loop_labels'}{$_},$self->{'black'});
850				}
851
852				$y_mod = $y_mod - 15;
853			}
854		}
855	}
856	$self->{'loop_count'}++;
857	return $self;
858}
859
8601;
861
862=head1 NAME
863
864Bio::Graphics::DrawTransmembrane - draw a cartoon of an Alpha-helical transmembrane protein.
865
866=head1 SYNOPSIS
867
868  use Bio::Graphics::DrawTransmembrane;
869  my @topology = (20,45,59,70,86,109,145,168,194,220);
870
871  ## Simple use - -topology is the only option that is required
872
873  my $im = Bio::Graphics::DrawTransmembrane->new(
874      -title => 'This is a cartoon displaying transmembrane helices.',
875      -topology => \@topology);
876
877  ## More advanced use
878  my %labels = (5 => '5 - Sulphation Site',
879                21 => '1st Helix',
880                47 => '40 - Mutation',
881                60 => 'Voltage Sensor',
882                72 => '72 - Mutation 2',
883                73 => '73 - Mutation 3',
884                138 => '138 - Glycosylation Site',
885                170 => '170 - Phosphorylation Site',
886                200 => 'Last Helix');
887
888  my $im = Bio::Graphics::DrawTransmembrane->new(-n_terminal=> 'out',
889                                  -topology => \@topology,
890                                  -bold_helices=> 1,
891                                  -labels=> \%labels,
892                                  -text_offset=> -15,
893                                  -outside_label=>'Lumen',
894                                  -inside_label=>'Cytoplasm',
895                                  -membrane_label=>'Membrane',
896                                  -vertical_padding=> 155);
897
898  ## Parse Tmhmm data
899  use Bio::Tools::Tmhmm;
900  my $im = Bio::Graphics::DrawTransmembrane->new(
901      -title=>'Let\'s parse some Tmhmm output...',
902      -bold_helices=> 1);
903  open(FILE, 'tmhmm.out');
904  my $parser = new Bio::Tools::Tmhmm(-fh => \*FILE );
905  while(my $tmhmm_feat = $parser->next_result ) {
906	## Load features into DrawTransmembrane object
907	$im->add_tmhmm_feat($tmhmm_feat);
908  }
909  close FILE;
910
911  ## Now write the image to a .png file
912  open(OUTPUT, ">output.png");
913  binmode OUTPUT;
914  print OUTPUT $im->png;
915  close OUTPUT;
916
917=head1 DESCRIPTION
918
919A module to draw a cartoon of an alpha-helical transmembrane
920protein. It uses GD and allows the image to be written to a .png file.
921
922The options are a set of tag/value pairs as follows:
923
924  Option              Value                                         Default
925  ------              -----                                         -------
926
927  -topology           Array containing transmembrane helix          none
928	              boundaries. This is the only option that
929		      is required
930
931  -topology_string    Alternative to -topology, provide a string    none
932                      containing the topology data in the form
933		      A.11,31;B.41,59;C.86,107;D.145,166
934
935  -n_terminal         Location of the N-terminal of the sequence,   out
936  	              either 'in' or 'out'
937
938  -title              Title to add to the image                     none
939
940  -inside_label       Label for the inside of the membrane          Cytoplasmic
941
942  -outside_label      Label for the outside of the membrane         Extracellular
943
944  -membrane_label     Label for the membrane                        Plasma Membrane
945
946  -colour_scheme      Colour scheme to use. Current choices are     blue
947                      blue, yellow, red, green, pink or white.
948
949  -labels             Label loops and helices using data from a     none
950                      hash, e.g.
951
952		      %labels = (138 => 'Glycosylation Site',
953		                 190 => 'Binding Site');
954
955		      The hash key must be numeric, ranges are
956		      not allowed.
957
958  -bold_helices       Draws black boxes round helices               1
959
960  -bold_labels        Draws black boxes round labels                0
961
962  -text_offset        Shift the text labeling the loops. Use a      0
963                      negative value to shift it left, a positive
964		      value to shift it right
965
966  -helix_height       Transmembrane helix height                    130
967
968  -helix_width        Transmembrane helix width                     50
969
970  -loop_width         Loop width                                    20
971
972  -vertical_padding   Vertical padding                              140
973
974  -horizontal_padding Horizontal Padding                            150
975
976  -membrane_offset    Offest between helix end and membrane         6
977
978  -short_loop_height  Height of short loops                         90
979
980  -medium_loop_height Height of medium loops                        120
981
982  -long_loop_height   Height of long loops                          150
983
984  -short_loop_limit   Length in residues below which a loop is      15
985                      classed as short
986
987  -long_loop_limit    Length in residues above which a loop is      30
988                      classed as long
989
990  -loop_heights       Explicitly set heights of each loop, e.g.
991
992                      %loop_heights = (1 => 45,
993                                       2 => 220,
994                                       3 => 50,
995                                       4 => 220,
996                                       9 => 70);
997
998                      The key corresponds to the loop number. Both
999                      key and value must be numeric. If you use
1000                      -loop_height and there is a defined height
1001                      for the current loop then other height values
1002                      will be overridden
1003
1004  -n_terminal_height  Height of N-terminal                          150
1005
1006  -c_terminal_height  Height of C-terminal                          80
1007
1008  -n_terminal_offset  Shift the N-terminal left by this amount      0
1009
1010  -c_terminal_offset  Shift the C-terminal right by this amount     0
1011
1012  -helix_label        Change the 'S' label on each helix. Only 1    S
1013                      character is allowed
1014
1015  -show_labels        Display text labels                           on
1016
1017  -draw_cytosol       Show the cytosol                              false
1018
1019  -draw_bilayer       Show the membrane                             true
1020
1021  -draw_loops         Show the loops                                true
1022
1023  -draw_terminai      Show the terminai                             true
1024
1025  -draw_helices       Show the helices                              true
1026
1027  -dontsort           Don't automatically sort the topology array   0
1028
1029  -ttf_font           Path to TTF font, e.g.                        none
1030                      /usr/share/fonts/msttcorefonts/arial.ttf
1031
1032  -ttf_font_size      Default size for TTF font. Use 7-9 with       8
1033                      Arial for best results
1034
1035
1036Height, width, padding and other numerical values can gernerally be
1037left alone. They are useful if your labels consists of a lot of text
1038as this may lead to them overlapping. In this case try increasing the
1039loop_width or helix_width options. -text_offset is also very useful
1040for avoiding overlapping.
1041
1042=head1 AUTHOR
1043
1044Tim Nugent E<lt>timnugent@gmail.comE<gt>
1045
1046=cut
1047