2 # wheel.pl: Draw alpha helical wheel with hydrophobic moment using
3 # wif (modifiable to woct) scale.
4 # Copyright (C) 2001 Don Armstrong and Raphael Zidovetzki
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 my $VERSION=q$Id: wheel.pl,v 1.3 2004-10-21 22:32:38 don Exp $;
25 # Intial Released Version 0.10
26 # p01: Fixing displayed angle
27 # p02: Added arrowhead, fixed lack of return in xy2deg(), added no color option
28 # p03: Added round function, fixed rounding in angles, changed hm to line, added hm_up option
29 # p04: Removed bounding box
30 # p05: Changing user interface to allow simultaneous viewing of options and wheel
31 # p06: Released to public under GPL
33 use CGI::Carp qw(fatalsToBrowser);
45 return (floor $a+0.5);
68 my $angle = rad2deg(atan($x/$y));
78 sub calculate_hydrophobic_moment($$){
79 my ($sequence,$param)=@_;
80 my $phe=$param->{phe};
81 my $theta=$param->{theta};
83 foreach my $amino_acid (split(//,$sequence)) {
84 $x+=-$param->{aa}->{$amino_acid}->{wif} * sin($phe);
85 $y+=+$param->{aa}->{$amino_acid}->{wif} * cos($phe);
93 my ($im,$aasequence,$param)=@_;
94 my $phe=$param->{phe};
98 foreach my $aminoacid (split(//,$aasequence)) {
99 push @points, calculate_geometry($param,$phe);
100 $phe += $param->{theta};
102 last if ($aanumber>18);
104 for ($aanumber=0;$aanumber<19;$aanumber++) {
105 my $point = pop @points;
106 last if (!defined $point);
108 draw_connection($im,$aanumber,$point1,$point);
112 $phe = $param->{phe};
114 foreach my $aminoacid (split(//,$aasequence)) {
115 $point1=calculate_geometry($param,$phe);
116 draw_aa($im,$aminoacid,
117 $param->{centerx}+($point1->{bx}-$param->{centerx})*
118 ($param->{d}+2.2*$param->{r}*POSIX::floor($aanumber/18))/
119 $param->{d},$param->{centery}+($point1->{by}-$param->{centery})*
120 ($param->{d}+2.2*$param->{r}*POSIX::floor($aanumber/18))/
121 $param->{d},$phe,$param,$aanumber);
122 $phe+=$param->{theta};
125 my ($hydro_x,$hydro_y) = calculate_hydrophobic_moment($aasequence,$param);
126 draw_hydrophobic_moment($im,$hydro_x,$hydro_y,$param);
129 sub draw_hydrophobic_moment($$$$){
130 my ($im,$x,$y,$param)=@_;
131 my $norm=($x*$x+$y*$y)**(0.5);
132 my $angle = xy2deg($x,-$y);
133 if ($norm !=0 and $param->{hmscale}!=0) {
134 $x=$x/$norm*$param->{hmscale};
135 $y=$y/$norm*$param->{hmscale};
138 # Draw Moment Line (Box, really)
139 my $poly = GD::Polygon->new;
140 print STDERR "$angle\n";
141 $poly->addPt($param->{centerx}-round(($param->{hmscale}/50)*sin(deg2rad($angle+90))),
142 $param->{centery}+round(($param->{hmscale}/50)*cos(deg2rad($angle+90))));
143 $poly->addPt($param->{centerx}+round($x*0.85-($param->{hmscale}/50)*sin(deg2rad($angle+90))),
144 $param->{centery}+round($y*0.85+($param->{hmscale}/50)*cos(deg2rad($angle+90))));
145 $poly->addPt($param->{centerx}+round($x*0.85-($param->{hmscale}/50)*sin(deg2rad($angle-90))),
146 $param->{centery}+round($y*0.85+($param->{hmscale}/50)*cos(deg2rad($angle-90))));
147 $poly->addPt($param->{centerx}-round(($param->{hmscale}/50)*sin(deg2rad($angle-90))),
148 $param->{centery}+round(($param->{hmscale}/50)*cos(deg2rad($angle-90))));
149 $im->filledPolygon($poly,$param->{black});
153 $poly = GD::Polygon->new;
154 $poly->addPt($param->{centerx}+$x,$param->{centery}+$y);
155 $poly->addPt($param->{centerx}+$x-round(($param->{hmscale}/5)*sin(deg2rad($angle+20))),
156 $param->{centery}+$y+round(($param->{hmscale}/5)*cos(deg2rad($angle+20))));
157 $poly->addPt($param->{centerx}+$x-round(($param->{hmscale}/5)*sin(deg2rad($angle-20))),
158 $param->{centery}+$y+round(($param->{hmscale}/5)*cos(deg2rad($angle-20))));
159 $im->filledPolygon($poly,$param->{black});
160 my $text= GD::Text::Align->new($im,
163 color=> $param->{black},
165 $text->set_font('/usr/share/fonts/truetype/Florsn01.ttf',$param->{fontsize2});
166 if ($param->{disp_hm_angle}) {
167 $text->set_text(POSIX::floor($norm*100+.5)/100 . '@' . POSIX::floor(($angle*10+0.5))/10);
170 $text->set_text(POSIX::floor($norm*100+.5)/100);
172 my $offset=-10-$param->{fontsize2}/2;
176 $text->draw($param->{centerx},$param->{centery}+$offset);
179 sub draw_connection($$$$) {
180 my ($im,$aanumber,$point1,$point2) = @_;
181 my $color=$im->colorAllocate(240*((18-$aanumber)/18),
182 240*((18-$aanumber)/18),
183 240*((18-$aanumber)/18)
185 my $poly = GD::Polygon->new;
186 $poly->addPt($point1->{ex},$point1->{ey});
187 $poly->addPt($point2->{ex},$point2->{ey});
188 $poly->addPt($point2->{cx},$point2->{cy});
189 $poly->addPt($point1->{cx},$point1->{cy});
190 $im->filledPolygon($poly,$color);
194 sub calculate_geometry($$){
196 return {bx=>($param->{d})*sin($phe)+$param->{centerx},
197 cx=>($param->{d}-($param->{w}/2)/sin((pi-$param->{theta})/2))*sin($phe)+$param->{centerx},
198 dx=>($param->{centerx}),
199 ex=>($param->{d}+($param->{w}/2)/sin((pi-$param->{theta})/2))*sin($phe)+$param->{centerx},
200 by=>-($param->{d})*cos($phe)+$param->{centery},
201 cy=>-($param->{d}-($param->{w}/2)/sin((pi-$param->{theta})/2))*cos($phe)+$param->{centery},
202 dy=>($param->{centery}),
203 ey=>-($param->{d}+($param->{w}/2)/sin((pi-$param->{theta})/2))*cos($phe)+$param->{centery},
208 sub draw_n_gon($$$$){
210 my $ngon=GD::Polygon->new;
211 for (my $v = 0;$v<$n;$v++) {
212 $ngon->addPt($x+$r*sin((2*pi/$n)*$v),$y-$r*cos((2*pi/$n)*$v));
217 sub draw_aa($$$$$$$){
218 my ($im,$aasymbol,$bx,$by,$phe,$param,$aanumber)=@_;
223 $_ = $param->{aa}->{$aasymbol}->{shape};
224 if (defined && /square/) {
225 $im->filledRectangle($ax-$r,$ay-$r,$ax+$r,$ay+$r,$param->{aa}->{$aasymbol}->{bcolor});
226 $im->rectangle($ax-$r,$ay-$r,$ax+$r,$ay+$r,$param->{aa}->{$aasymbol}->{fcolor});
228 elsif (defined && /triangle/) {
229 $im->filledPolygon(draw_n_gon($ax,$ay,1.25*$r,3),
230 $param->{aa}->{$aasymbol}->{bcolor});
231 $im->polygon(draw_n_gon($ax,$ay,1.25*$r,3),
232 $param->{aa}->{$aasymbol}->{fcolor});
234 elsif (defined && /hexagon/) {
235 $im->filledPolygon(draw_n_gon($ax,$ay,1.18*$r,6),
236 $param->{aa}->{$aasymbol}->{bcolor});
237 $im->polygon(draw_n_gon($ax,$ay,1.18*$r,6),
238 $param->{aa}->{$aasymbol}->{fcolor});
240 elsif (defined && /diamond/) {
241 $im->filledPolygon(draw_n_gon($ax,$ay,1.25*$r,4),
242 $param->{aa}->{$aasymbol}->{bcolor});
243 $im->polygon(draw_n_gon($ax,$ay,1.25*$r,4),
244 $param->{aa}->{$aasymbol}->{fcolor});
246 elsif (defined && /pentagon/) {
247 $im->filledPolygon(draw_n_gon($ax,$ay,1.2*$r,5),
248 $param->{aa}->{$aasymbol}->{bcolor});
249 $im->polygon(draw_n_gon($ax,$ay,1.2*$r,5),
250 $param->{aa}->{$aasymbol}->{fcolor});
252 elsif (defined && /octagon/){
253 $im->filledPolygon(draw_n_gon($ax,$ay,1.1*$r,8),
254 $param->{aa}->{$aasymbol}->{bcolor});
255 $im->polygon(draw_n_gon($ax,$ay,1.1*$r,8),
256 $param->{aa}->{$aasymbol}->{fcolor});
259 $im->filledPolygon(draw_n_gon($ax,$ay,$r*0.99,360),
260 $param->{aa}->{$aasymbol}->{bcolor});
261 $im->arc($ax,$ay,2*$r,2*$r,0,360,$param->{aa}->{$aasymbol}->{fcolor});
263 $im->fill($ax,$ay,$param->{aa}->{$aasymbol}->{bcolor});
264 my $text= GD::Text::Align->new($im,
267 color=> $param->{black},
269 $text->set_font('/usr/share/fonts/truetype/Florsn01.ttf',$param->{fontsize});
270 my $label_text=$aasymbol . abs($aanumber+$param->{indexaa});
271 $text->set_text($label_text);
272 $text->draw($ax,$ay,0);
276 if (defined $q->param('draw') and $q->param('draw')=~/yes/) {
296 # Merge in query params
297 # DGwoct[woct] and DGwif[wif] are octanol and interface hydrophobicity values
298 # as seen on http://blanco.biomol.uci.edu/Whole_residue_HFscales.txt
301 # Nature Struc. Biol 3:842 (1996) [wif values]
302 # Wimley, Creamer and White
303 # Biochemistry 34:5108 (1996) [woct values]
305 $param->{aa} = {G=>{name=>'Glycine',
326 I=>{name=>'Isoleucine',
332 M=>{name=>'Methionine',
343 F=>{name=>'Phenylalanine',
349 W=>{name=>'Tryptophan',
360 T=>{name=>'Threonine',
365 N=>{name=>'Asparagine',
370 Q=>{name=>'Glutamine',
375 Y=>{name=>'Tyrosine',
381 C=>{name=>'Cysteine',
393 border=>[187,187,255],
395 R=>{name=>'Arginine',
401 border=>[187,187,255],
403 H=>{name=>'Histidine',
409 border=>[187,187,255],
411 D=>{name=>'Aspartic Acid',
417 border=>[187,187,255],
419 E=>{name=>'Glutamic Acid',
425 border=>[187,187,255],
428 foreach my $param_key ($q->param) {
429 my $param_value=$q->param($param_key);
430 if ($param_value=~/^(\-{0,1}\d*\.{0,1}\d+)$/) {
431 $param->{$param_key}=$q->param($param_key);
432 if ($param_key=~/^(phe|theta|hmdisp)$/) {
433 $param->{$param_key}=$param->{$param_key}/180*pi;
437 if ($q->param('disp_hm_angle') =~ /on/) {
438 $param->{'disp_hm_angle'}=1;
441 $param->{'disp_hm_angle'}=0;
443 if ($q->param('reverse_helix') =~ /on/) {
444 $param->{'reverse_helix'}=1;
447 $param->{'reverse_helix'}=0;
449 if ($q->param('wo_color') =~ /on/) {
450 $param->{'wifcolor'}=0;
453 $param->{'wifcolor'}=1;
455 my $im = new GD::Image($param->{xsize},$param->{ysize});
456 $param->{white} = $im->colorAllocate(255,255,255);
457 $param->{black} = $im->colorAllocate(0,0,0);
458 $param->{red} = $im->colorAllocate(255,0,0);
459 $param->{blue} = $im->colorAllocate(0,0,255);
460 foreach my $aminoacid (keys %{$param->{aa}}) {
461 if ($param->{wifcolor} && $param->{aa}->{$aminoacid}->{wif}<=$param->{maxwif} && !(defined $param->{aa}->{$aminoacid}->{charged} && $param->{aa}->{$aminoacid}->{charged})) {
462 $param->{aa}->{$aminoacid}->{fcolor} =
463 $im->colorAllocate(min(($param->{aa}->{$aminoacid}->{wif}-$param->{minwif})/
464 (0-$param->{minwif})*255,255),
465 255-max(($param->{aa}->{$aminoacid}->{wif})/
466 ($param->{maxwif}),0)*255,0);
468 elsif ($param->{wifcolor} && defined $param->{aa}->{$aminoacid}->{border}) {
469 $param->{aa}->{$aminoacid}->{fcolor} = $im->colorAllocate($param->{aa}->{$aminoacid}->{border}[0],
470 $param->{aa}->{$aminoacid}->{border}[1],
471 $param->{aa}->{$aminoacid}->{border}[2]);
474 $param->{aa}->{$aminoacid}->{fcolor} = $param->{black};
476 if ($param->{wifcolor} && $param->{aa}->{$aminoacid}->{wif}<=$param->{maxwif} && !(defined $param->{aa}->{$aminoacid}->{charged} && $param->{aa}->{$aminoacid}->{charged})) {
477 $param->{aa}->{$aminoacid}->{bcolor} =
478 $im->colorAllocate(min(($param->{aa}->{$aminoacid}->{wif}-$param->{minwif})/
479 (0-$param->{minwif})*255,255),
480 255-max(($param->{aa}->{$aminoacid}->{wif})/
481 ($param->{maxwif}),0)*255,0);
483 elsif ($param->{wifcolor} && defined $param->{aa}->{$aminoacid}->{fill}) {
484 $param->{aa}->{$aminoacid}->{bcolor} = $im->colorAllocate($param->{aa}->{$aminoacid}->{fill}[0],
485 $param->{aa}->{$aminoacid}->{fill}[1],
486 $param->{aa}->{$aminoacid}->{fill}[2]);
489 $param->{aa}->{$aminoacid}->{bcolor} = $param->{white};
493 # $im->rectangle(0,0,$param->{xsize}-1,$param->{ysize}-1,$param->{black});
494 my $aasequence = $q->param('sequence');
495 $aasequence =~ s/[^AC-IK-NP-TV-WY]//gs;
496 if ($param->{reverse_helix}) {
497 my $aaseq=$aasequence;
498 $param->{indexaa}*=-1;
501 foreach my $aa(split(//,$aaseq)) {
502 $aasequence = $aa . $aasequence;
505 $param->{indexaa}-=$numberofaa-1;
508 print STDERR "hmdisp: $param->{hmdisp}\n";
509 if ($param->{'hmdisp'} =~ /^(\-{0,1}\d*\.{0,1}\d+)$/) {
510 my ($x,$y) = calculate_hydrophobic_moment($aasequence,$param);
511 $param->{phe} = - deg2rad(xy2deg($x,-$y)) + $param->{'hmdisp'};
512 print STDERR $param->{phe};
514 draw_wheel($im,$aasequence,$param);
515 print $q->header(-type=>'image/png');
522 print $q->start_html('Helical Wheel Projections');
523 if (defined $q->param('submit') and $q->param('submit')=~/Submit/) {
524 print $q->h1('Wheel:'.$q->param('sequence'));
525 print $q->img({-src=>$q->self_url.'&draw=yes'});
528 <table width="400"><tr><td>By default the output presents the
529 hydrophilic residues as circles, hydrophobic residues as diamonds,
530 potentially negatively charged as triangles, and potentially
531 positively charged as pentagons. Hydrophobicity is color coded as
532 well: the most hydrophobic residue is green, and the amount of green
533 is decreasing proportionally to the hydrophobicity, with zero
534 hydrophobicity coded as yellow. Hydrophilic residues are coded red
535 with pure red being the most hydrophilic (uncharged) residue, and the
536 amount of red decreasing proportionally to the hydrophilicity. The
537 potentially charged residues are light blue. (The color will not apply
538 if you turn off color.)</td></tr></table>
542 print $q->h1('Helical Wheel Projections'),
543 $q->start_form(-method=>'GET'),
545 $q->td({-bgcolor=>'#ddddff'},['Sequence:',
546 $q->textarea(-name=>'sequence',
550 $q->td({-bgcolor=>'#bbbbff'},['Initial AA Number:',
551 $q->textfield(-name=>'indexaa',
556 $q->td({-bgcolor=>'#ddddff'},['Initial AA Rotation:',
557 $q->textfield(-name=>'phe',
561 $q->td({-bgcolor=>'#ddddff'},['Per AA Rotation:',
562 $q->textfield(-name=>'theta',
566 $q->td({-bgcolor=>'#bbbbff'},['Xsize:',
567 $q->textfield(-name=>'xsize',
571 $q->td({-bgcolor=>'#ddddff'},['Ysize:',
572 $q->textfield(-name=>'ysize',
576 $q->td({-bgcolor=>'#bbbbff'},['CenterX:',
577 $q->textfield(-name=>'centerx',
581 $q->td({-bgcolor=>'#ddddff'},['CenterY:',
582 $q->textfield(-name=>'centery',
586 $q->td({-bgcolor=>'#bbbbff'},['Connection Width:',
587 $q->textfield(-name=>'w',
591 $q->td({-bgcolor=>'#ddddff'},['Symbol Size:',
592 $q->textfield(-name=>'r',
596 $q->td({-bgcolor=>'#bbbbff'},['Font Size:',
597 $q->textfield(-name=>'fontsize',
601 $q->td({-bgcolor=>'#ddddff'},['Graph Radius:',
602 $q->textfield(-name=>'d',
606 $q->td({-bgcolor=>'#bbbbff'},['HM Font Size:',
607 $q->textfield(-name=>'fontsize2',
611 $q->td({-bgcolor=>'#ddddff'},['HM Scale:',
612 $q->textfield(-name=>'hmscale',
616 $q->td({-bgcolor=>'#bbbbff'},['Display HM Angle:',
617 $q->checkbox(-name=>'disp_hm_angle',
621 $q->td({-bgcolor=>'#ddddff'},['Reverse Helix:',
622 $q->checkbox(-name=>'reverse_helix',
625 $q->td({-bgcolor=>'#bbbbff'},['No Color:',
626 $q->checkbox(-name=>'wo_color',
629 $q->td({-bgcolor=>'#ddddff'},['Hydrophobic Moment Displacement:',
630 $q->textfield(-name=>'hmdisp',
634 $q->td({-bgcolor=>'#bbbbff',-colspan=>2},
635 [$q->center($q->submit(-name=>'submit',-value=>'Submit'))
640 $q->hr,$q->i("Created by ".$q->a({-href=>"mailto:don\@donarmstrong.com"},'Don Armstrong')." and Raphael Zidovetzki. Version: ".$q->b($VERSION));