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.4 2009-10-20 21:23:36 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);
42 use HTML::Entities qw(encode_entities);
47 return (floor $a+0.5);
70 my $angle = rad2deg(atan($x/$y));
80 sub calculate_hydrophobic_moment($$){
81 my ($sequence,$param)=@_;
82 my $phe=$param->{phe};
83 my $theta=$param->{theta};
85 foreach my $amino_acid (split(//,$sequence)) {
86 $x+=-$param->{aa}->{$amino_acid}->{wif} * sin($phe);
87 $y+=+$param->{aa}->{$amino_acid}->{wif} * cos($phe);
95 my ($im,$aasequence,$param)=@_;
96 my $phe=$param->{phe};
100 foreach my $aminoacid (split(//,$aasequence)) {
101 push @points, calculate_geometry($param,$phe);
102 $phe += $param->{theta};
104 last if ($aanumber>18);
106 for ($aanumber=0;$aanumber<19;$aanumber++) {
107 my $point = pop @points;
108 last if (!defined $point);
110 draw_connection($im,$aanumber,$point1,$point);
114 $phe = $param->{phe};
116 foreach my $aminoacid (split(//,$aasequence)) {
117 $point1=calculate_geometry($param,$phe);
118 draw_aa($im,$aminoacid,
119 $param->{centerx}+($point1->{bx}-$param->{centerx})*
120 ($param->{d}+2.2*$param->{r}*POSIX::floor($aanumber/18))/
121 $param->{d},$param->{centery}+($point1->{by}-$param->{centery})*
122 ($param->{d}+2.2*$param->{r}*POSIX::floor($aanumber/18))/
123 $param->{d},$phe,$param,$aanumber);
124 $phe+=$param->{theta};
127 my ($hydro_x,$hydro_y) = calculate_hydrophobic_moment($aasequence,$param);
128 draw_hydrophobic_moment($im,$hydro_x,$hydro_y,$param);
131 sub draw_hydrophobic_moment($$$$){
132 my ($im,$x,$y,$param)=@_;
133 my $norm=($x*$x+$y*$y)**(0.5);
134 my $angle = xy2deg($x,-$y);
135 if ($norm !=0 and $param->{hmscale}!=0) {
136 $x=$x/$norm*$param->{hmscale};
137 $y=$y/$norm*$param->{hmscale};
140 # Draw Moment Line (Box, really)
141 my $poly = GD::Polygon->new;
142 print STDERR "$angle\n";
143 $poly->addPt($param->{centerx}-round(($param->{hmscale}/50)*sin(deg2rad($angle+90))),
144 $param->{centery}+round(($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($x*0.85-($param->{hmscale}/50)*sin(deg2rad($angle-90))),
148 $param->{centery}+round($y*0.85+($param->{hmscale}/50)*cos(deg2rad($angle-90))));
149 $poly->addPt($param->{centerx}-round(($param->{hmscale}/50)*sin(deg2rad($angle-90))),
150 $param->{centery}+round(($param->{hmscale}/50)*cos(deg2rad($angle-90))));
151 $im->filledPolygon($poly,$param->{black});
155 $poly = GD::Polygon->new;
156 $poly->addPt($param->{centerx}+$x,$param->{centery}+$y);
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 $poly->addPt($param->{centerx}+$x-round(($param->{hmscale}/5)*sin(deg2rad($angle-20))),
160 $param->{centery}+$y+round(($param->{hmscale}/5)*cos(deg2rad($angle-20))));
161 $im->filledPolygon($poly,$param->{black});
162 my $text= GD::Text::Align->new($im,
165 color=> $param->{black},
167 $text->set_font('/usr/share/fonts/truetype/Florsn01.ttf',$param->{fontsize2});
168 if ($param->{disp_hm_angle}) {
169 $text->set_text(POSIX::floor($norm*100+.5)/100 . '@' . POSIX::floor(($angle*10+0.5))/10);
172 $text->set_text(POSIX::floor($norm*100+.5)/100);
174 my $offset=-10-$param->{fontsize2}/2;
178 $text->draw($param->{centerx},$param->{centery}+$offset);
181 sub draw_connection($$$$) {
182 my ($im,$aanumber,$point1,$point2) = @_;
183 my $color=$im->colorAllocate(240*((18-$aanumber)/18),
184 240*((18-$aanumber)/18),
185 240*((18-$aanumber)/18)
187 my $poly = GD::Polygon->new;
188 $poly->addPt($point1->{ex},$point1->{ey});
189 $poly->addPt($point2->{ex},$point2->{ey});
190 $poly->addPt($point2->{cx},$point2->{cy});
191 $poly->addPt($point1->{cx},$point1->{cy});
192 $im->filledPolygon($poly,$color);
196 sub calculate_geometry($$){
198 return {bx=>($param->{d})*sin($phe)+$param->{centerx},
199 cx=>($param->{d}-($param->{w}/2)/sin((pi-$param->{theta})/2))*sin($phe)+$param->{centerx},
200 dx=>($param->{centerx}),
201 ex=>($param->{d}+($param->{w}/2)/sin((pi-$param->{theta})/2))*sin($phe)+$param->{centerx},
202 by=>-($param->{d})*cos($phe)+$param->{centery},
203 cy=>-($param->{d}-($param->{w}/2)/sin((pi-$param->{theta})/2))*cos($phe)+$param->{centery},
204 dy=>($param->{centery}),
205 ey=>-($param->{d}+($param->{w}/2)/sin((pi-$param->{theta})/2))*cos($phe)+$param->{centery},
210 sub draw_n_gon($$$$){
212 my $ngon=GD::Polygon->new;
213 for (my $v = 0;$v<$n;$v++) {
214 $ngon->addPt($x+$r*sin((2*pi/$n)*$v),$y-$r*cos((2*pi/$n)*$v));
219 sub draw_aa($$$$$$$){
220 my ($im,$aasymbol,$bx,$by,$phe,$param,$aanumber)=@_;
225 $_ = $param->{aa}->{$aasymbol}->{shape};
226 if (defined && /square/) {
227 $im->filledRectangle($ax-$r,$ay-$r,$ax+$r,$ay+$r,$param->{aa}->{$aasymbol}->{bcolor});
228 $im->rectangle($ax-$r,$ay-$r,$ax+$r,$ay+$r,$param->{aa}->{$aasymbol}->{fcolor});
230 elsif (defined && /triangle/) {
231 $im->filledPolygon(draw_n_gon($ax,$ay,1.25*$r,3),
232 $param->{aa}->{$aasymbol}->{bcolor});
233 $im->polygon(draw_n_gon($ax,$ay,1.25*$r,3),
234 $param->{aa}->{$aasymbol}->{fcolor});
236 elsif (defined && /hexagon/) {
237 $im->filledPolygon(draw_n_gon($ax,$ay,1.18*$r,6),
238 $param->{aa}->{$aasymbol}->{bcolor});
239 $im->polygon(draw_n_gon($ax,$ay,1.18*$r,6),
240 $param->{aa}->{$aasymbol}->{fcolor});
242 elsif (defined && /diamond/) {
243 $im->filledPolygon(draw_n_gon($ax,$ay,1.25*$r,4),
244 $param->{aa}->{$aasymbol}->{bcolor});
245 $im->polygon(draw_n_gon($ax,$ay,1.25*$r,4),
246 $param->{aa}->{$aasymbol}->{fcolor});
248 elsif (defined && /pentagon/) {
249 $im->filledPolygon(draw_n_gon($ax,$ay,1.2*$r,5),
250 $param->{aa}->{$aasymbol}->{bcolor});
251 $im->polygon(draw_n_gon($ax,$ay,1.2*$r,5),
252 $param->{aa}->{$aasymbol}->{fcolor});
254 elsif (defined && /octagon/){
255 $im->filledPolygon(draw_n_gon($ax,$ay,1.1*$r,8),
256 $param->{aa}->{$aasymbol}->{bcolor});
257 $im->polygon(draw_n_gon($ax,$ay,1.1*$r,8),
258 $param->{aa}->{$aasymbol}->{fcolor});
261 $im->filledPolygon(draw_n_gon($ax,$ay,$r*0.99,360),
262 $param->{aa}->{$aasymbol}->{bcolor});
263 $im->arc($ax,$ay,2*$r,2*$r,0,360,$param->{aa}->{$aasymbol}->{fcolor});
265 $im->fill($ax,$ay,$param->{aa}->{$aasymbol}->{bcolor});
266 my $text= GD::Text::Align->new($im,
269 color=> $param->{black},
271 $text->set_font('/usr/share/fonts/truetype/Florsn01.ttf',$param->{fontsize});
272 my $label_text=$aasymbol . abs($aanumber+$param->{indexaa});
273 $text->set_text($label_text);
274 $text->draw($ax,$ay,0);
278 if (defined $q->param('draw') and $q->param('draw')=~/yes/) {
298 # Merge in query params
299 # DGwoct[woct] and DGwif[wif] are octanol and interface hydrophobicity values
300 # as seen on http://blanco.biomol.uci.edu/Whole_residue_HFscales.txt
303 # Nature Struc. Biol 3:842 (1996) [wif values]
304 # Wimley, Creamer and White
305 # Biochemistry 34:5108 (1996) [woct values]
307 $param->{aa} = {G=>{name=>'Glycine',
328 I=>{name=>'Isoleucine',
334 M=>{name=>'Methionine',
345 F=>{name=>'Phenylalanine',
351 W=>{name=>'Tryptophan',
362 T=>{name=>'Threonine',
367 N=>{name=>'Asparagine',
372 Q=>{name=>'Glutamine',
377 Y=>{name=>'Tyrosine',
383 C=>{name=>'Cysteine',
395 border=>[187,187,255],
397 R=>{name=>'Arginine',
403 border=>[187,187,255],
405 H=>{name=>'Histidine',
411 border=>[187,187,255],
413 D=>{name=>'Aspartic Acid',
419 border=>[187,187,255],
421 E=>{name=>'Glutamic Acid',
427 border=>[187,187,255],
430 foreach my $param_key ($q->param) {
431 my $param_value=$q->param($param_key);
432 if ($param_value=~/^(\-{0,1}\d*\.{0,1}\d+)$/) {
433 $param->{$param_key}=$q->param($param_key);
434 if ($param_key=~/^(phe|theta|hmdisp)$/) {
435 $param->{$param_key}=$param->{$param_key}/180*pi;
439 if ($q->param('disp_hm_angle') =~ /on/) {
440 $param->{'disp_hm_angle'}=1;
443 $param->{'disp_hm_angle'}=0;
445 if ($q->param('reverse_helix') =~ /on/) {
446 $param->{'reverse_helix'}=1;
449 $param->{'reverse_helix'}=0;
451 if ($q->param('wo_color') =~ /on/) {
452 $param->{'wifcolor'}=0;
455 $param->{'wifcolor'}=1;
457 my $im = new GD::Image($param->{xsize},$param->{ysize});
458 $param->{white} = $im->colorAllocate(255,255,255);
459 $param->{black} = $im->colorAllocate(0,0,0);
460 $param->{red} = $im->colorAllocate(255,0,0);
461 $param->{blue} = $im->colorAllocate(0,0,255);
462 foreach my $aminoacid (keys %{$param->{aa}}) {
463 if ($param->{wifcolor} && $param->{aa}->{$aminoacid}->{wif}<=$param->{maxwif} && !(defined $param->{aa}->{$aminoacid}->{charged} && $param->{aa}->{$aminoacid}->{charged})) {
464 $param->{aa}->{$aminoacid}->{fcolor} =
465 $im->colorAllocate(min(($param->{aa}->{$aminoacid}->{wif}-$param->{minwif})/
466 (0-$param->{minwif})*255,255),
467 255-max(($param->{aa}->{$aminoacid}->{wif})/
468 ($param->{maxwif}),0)*255,0);
470 elsif ($param->{wifcolor} && defined $param->{aa}->{$aminoacid}->{border}) {
471 $param->{aa}->{$aminoacid}->{fcolor} = $im->colorAllocate($param->{aa}->{$aminoacid}->{border}[0],
472 $param->{aa}->{$aminoacid}->{border}[1],
473 $param->{aa}->{$aminoacid}->{border}[2]);
476 $param->{aa}->{$aminoacid}->{fcolor} = $param->{black};
478 if ($param->{wifcolor} && $param->{aa}->{$aminoacid}->{wif}<=$param->{maxwif} && !(defined $param->{aa}->{$aminoacid}->{charged} && $param->{aa}->{$aminoacid}->{charged})) {
479 $param->{aa}->{$aminoacid}->{bcolor} =
480 $im->colorAllocate(min(($param->{aa}->{$aminoacid}->{wif}-$param->{minwif})/
481 (0-$param->{minwif})*255,255),
482 255-max(($param->{aa}->{$aminoacid}->{wif})/
483 ($param->{maxwif}),0)*255,0);
485 elsif ($param->{wifcolor} && defined $param->{aa}->{$aminoacid}->{fill}) {
486 $param->{aa}->{$aminoacid}->{bcolor} = $im->colorAllocate($param->{aa}->{$aminoacid}->{fill}[0],
487 $param->{aa}->{$aminoacid}->{fill}[1],
488 $param->{aa}->{$aminoacid}->{fill}[2]);
491 $param->{aa}->{$aminoacid}->{bcolor} = $param->{white};
495 # $im->rectangle(0,0,$param->{xsize}-1,$param->{ysize}-1,$param->{black});
496 my $aasequence = $q->param('sequence');
497 $aasequence =~ s/[^AC-IK-NP-TV-WY]//gs;
498 if ($param->{reverse_helix}) {
499 my $aaseq=$aasequence;
500 $param->{indexaa}*=-1;
503 foreach my $aa(split(//,$aaseq)) {
504 $aasequence = $aa . $aasequence;
507 $param->{indexaa}-=$numberofaa-1;
510 print STDERR "hmdisp: $param->{hmdisp}\n";
511 if ($param->{'hmdisp'} =~ /^(\-{0,1}\d*\.{0,1}\d+)$/) {
512 my ($x,$y) = calculate_hydrophobic_moment($aasequence,$param);
513 $param->{phe} = - deg2rad(xy2deg($x,-$y)) + $param->{'hmdisp'};
514 print STDERR $param->{phe};
516 draw_wheel($im,$aasequence,$param);
517 print $q->header(-type=>'image/png');
524 print $q->start_html('Helical Wheel Projections');
525 if (defined $q->param('submit') and $q->param('submit')=~/Submit/) {
526 print $q->h1('Wheel:'.encode_entities($q->param('sequence')));
527 print $q->img({-src=>$q->self_url.'&draw=yes'});
530 <table width="400"><tr><td>By default the output presents the
531 hydrophilic residues as circles, hydrophobic residues as diamonds,
532 potentially negatively charged as triangles, and potentially
533 positively charged as pentagons. Hydrophobicity is color coded as
534 well: the most hydrophobic residue is green, and the amount of green
535 is decreasing proportionally to the hydrophobicity, with zero
536 hydrophobicity coded as yellow. Hydrophilic residues are coded red
537 with pure red being the most hydrophilic (uncharged) residue, and the
538 amount of red decreasing proportionally to the hydrophilicity. The
539 potentially charged residues are light blue. (The color will not apply
540 if you turn off color.)</td></tr></table>
544 print $q->h1('Helical Wheel Projections'),
545 $q->start_form(-method=>'GET'),
547 $q->td({-bgcolor=>'#ddddff'},['Sequence:',
548 $q->textarea(-name=>'sequence',
552 $q->td({-bgcolor=>'#bbbbff'},['Initial AA Number:',
553 $q->textfield(-name=>'indexaa',
558 $q->td({-bgcolor=>'#ddddff'},['Initial AA Rotation:',
559 $q->textfield(-name=>'phe',
563 $q->td({-bgcolor=>'#ddddff'},['Per AA Rotation:',
564 $q->textfield(-name=>'theta',
568 $q->td({-bgcolor=>'#bbbbff'},['Xsize:',
569 $q->textfield(-name=>'xsize',
573 $q->td({-bgcolor=>'#ddddff'},['Ysize:',
574 $q->textfield(-name=>'ysize',
578 $q->td({-bgcolor=>'#bbbbff'},['CenterX:',
579 $q->textfield(-name=>'centerx',
583 $q->td({-bgcolor=>'#ddddff'},['CenterY:',
584 $q->textfield(-name=>'centery',
588 $q->td({-bgcolor=>'#bbbbff'},['Connection Width:',
589 $q->textfield(-name=>'w',
593 $q->td({-bgcolor=>'#ddddff'},['Symbol Size:',
594 $q->textfield(-name=>'r',
598 $q->td({-bgcolor=>'#bbbbff'},['Font Size:',
599 $q->textfield(-name=>'fontsize',
603 $q->td({-bgcolor=>'#ddddff'},['Graph Radius:',
604 $q->textfield(-name=>'d',
608 $q->td({-bgcolor=>'#bbbbff'},['HM Font Size:',
609 $q->textfield(-name=>'fontsize2',
613 $q->td({-bgcolor=>'#ddddff'},['HM Scale:',
614 $q->textfield(-name=>'hmscale',
618 $q->td({-bgcolor=>'#bbbbff'},['Display HM Angle:',
619 $q->checkbox(-name=>'disp_hm_angle',
623 $q->td({-bgcolor=>'#ddddff'},['Reverse Helix:',
624 $q->checkbox(-name=>'reverse_helix',
627 $q->td({-bgcolor=>'#bbbbff'},['No Color:',
628 $q->checkbox(-name=>'wo_color',
631 $q->td({-bgcolor=>'#ddddff'},['Hydrophobic Moment Displacement:',
632 $q->textfield(-name=>'hmdisp',
636 $q->td({-bgcolor=>'#bbbbff',-colspan=>2},
637 [$q->center($q->submit(-name=>'submit',-value=>'Submit'))
642 $q->hr,$q->i("Created by ".$q->a({-href=>"mailto:don\@donarmstrong.com"},'Don Armstrong')." and Raphael Zidovetzki. Version: ".$q->b($VERSION));