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="0.10 p06 12/14/2001 DLA";
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',
371 border=>[187,187,255],
374 Q=>{name=>'Glutamine',
379 Y=>{name=>'Tyrosine',
385 C=>{name=>'Cysteine',
397 border=>[187,187,255],
399 R=>{name=>'Arginine',
405 border=>[187,187,255],
407 H=>{name=>'Histidine',
413 border=>[187,187,255],
415 D=>{name=>'Aspartic Acid',
421 border=>[187,187,255],
423 E=>{name=>'Glutamic Acid',
429 border=>[187,187,255],
432 foreach my $param_key ($q->param) {
433 my $param_value=$q->param($param_key);
434 if ($param_value=~/^(\-{0,1}\d*\.{0,1}\d+)$/) {
435 $param->{$param_key}=$q->param($param_key);
436 if ($param_key=~/^(phe|theta|hmdisp)$/) {
437 $param->{$param_key}=$param->{$param_key}/180*pi;
441 if ($q->param('disp_hm_angle') =~ /on/) {
442 $param->{'disp_hm_angle'}=1;
445 $param->{'disp_hm_angle'}=0;
447 if ($q->param('reverse_helix') =~ /on/) {
448 $param->{'reverse_helix'}=1;
451 $param->{'reverse_helix'}=0;
453 if ($q->param('wo_color') =~ /on/) {
454 $param->{'wifcolor'}=0;
457 $param->{'wifcolor'}=1;
459 my $im = new GD::Image($param->{xsize},$param->{ysize});
460 $param->{white} = $im->colorAllocate(255,255,255);
461 $param->{black} = $im->colorAllocate(0,0,0);
462 $param->{red} = $im->colorAllocate(255,0,0);
463 $param->{blue} = $im->colorAllocate(0,0,255);
464 foreach my $aminoacid (keys %{$param->{aa}}) {
465 if ($param->{wifcolor} && $param->{aa}->{$aminoacid}->{wif}<=$param->{maxwif} && !(defined $param->{aa}->{$aminoacid}->{charged} && $param->{aa}->{$aminoacid}->{charged})) {
466 $param->{aa}->{$aminoacid}->{fcolor} =
467 $im->colorAllocate(min(($param->{aa}->{$aminoacid}->{wif}-$param->{minwif})/
468 (0-$param->{minwif})*255,255),
469 255-max(($param->{aa}->{$aminoacid}->{wif})/
470 ($param->{maxwif}),0)*255,0);
472 elsif ($param->{wifcolor} && defined $param->{aa}->{$aminoacid}->{border}) {
473 $param->{aa}->{$aminoacid}->{fcolor} = $im->colorAllocate($param->{aa}->{$aminoacid}->{border}[0],
474 $param->{aa}->{$aminoacid}->{border}[1],
475 $param->{aa}->{$aminoacid}->{border}[2]);
478 $param->{aa}->{$aminoacid}->{fcolor} = $param->{black};
480 if ($param->{wifcolor} && $param->{aa}->{$aminoacid}->{wif}<=$param->{maxwif} && !(defined $param->{aa}->{$aminoacid}->{charged} && $param->{aa}->{$aminoacid}->{charged})) {
481 $param->{aa}->{$aminoacid}->{bcolor} =
482 $im->colorAllocate(min(($param->{aa}->{$aminoacid}->{wif}-$param->{minwif})/
483 (0-$param->{minwif})*255,255),
484 255-max(($param->{aa}->{$aminoacid}->{wif})/
485 ($param->{maxwif}),0)*255,0);
487 elsif ($param->{wifcolor} && defined $param->{aa}->{$aminoacid}->{fill}) {
488 $param->{aa}->{$aminoacid}->{bcolor} = $im->colorAllocate($param->{aa}->{$aminoacid}->{fill}[0],
489 $param->{aa}->{$aminoacid}->{fill}[1],
490 $param->{aa}->{$aminoacid}->{fill}[2]);
493 $param->{aa}->{$aminoacid}->{bcolor} = $param->{white};
497 # $im->rectangle(0,0,$param->{xsize}-1,$param->{ysize}-1,$param->{black});
498 my $aasequence = $q->param('sequence');
499 $aasequence =~ s/[^AC-IK-NP-TV-WY]//gs;
500 if ($param->{reverse_helix}) {
501 my $aaseq=$aasequence;
502 $param->{indexaa}*=-1;
505 foreach my $aa(split(//,$aaseq)) {
506 $aasequence = $aa . $aasequence;
509 $param->{indexaa}-=$numberofaa-1;
512 print STDERR "hmdisp: $param->{hmdisp}\n";
513 if ($param->{'hmdisp'} =~ /^(\-{0,1}\d*\.{0,1}\d+)$/) {
514 my ($x,$y) = calculate_hydrophobic_moment($aasequence,$param);
515 $param->{phe} = - deg2rad(xy2deg($x,-$y)) + $param->{'hmdisp'};
516 print STDERR $param->{phe};
518 draw_wheel($im,$aasequence,$param);
519 print $q->header(-type=>'image/png');
526 print $q->start_html('Helical Wheel Projections');
527 if (defined $q->param('submit') and $q->param('submit')=~/Submit/) {
528 print $q->h1('Wheel:'.$q->param('sequence'));
529 print $q->img({-src=>$q->self_url.'&draw=yes'});
531 print $q->h1('Helical Wheel Projections'),
532 $q->start_form(-method=>'GET'),
534 $q->td({-bgcolor=>'#ddddff'},['Sequence:',
535 $q->textarea(-name=>'sequence',
539 $q->td({-bgcolor=>'#bbbbff'},['Initial AA Number:',
540 $q->textfield(-name=>'indexaa',
545 $q->td({-bgcolor=>'#ddddff'},['Initial AA Rotation:',
546 $q->textfield(-name=>'phe',
550 $q->td({-bgcolor=>'#ddddff'},['Per AA Rotation:',
551 $q->textfield(-name=>'theta',
555 $q->td({-bgcolor=>'#bbbbff'},['Xsize:',
556 $q->textfield(-name=>'xsize',
560 $q->td({-bgcolor=>'#ddddff'},['Ysize:',
561 $q->textfield(-name=>'ysize',
565 $q->td({-bgcolor=>'#bbbbff'},['CenterX:',
566 $q->textfield(-name=>'centerx',
570 $q->td({-bgcolor=>'#ddddff'},['CenterY:',
571 $q->textfield(-name=>'centery',
575 $q->td({-bgcolor=>'#bbbbff'},['Connection Width:',
576 $q->textfield(-name=>'w',
580 $q->td({-bgcolor=>'#ddddff'},['Symbol Size:',
581 $q->textfield(-name=>'r',
585 $q->td({-bgcolor=>'#bbbbff'},['Font Size:',
586 $q->textfield(-name=>'fontsize',
590 $q->td({-bgcolor=>'#ddddff'},['Graph Radius:',
591 $q->textfield(-name=>'d',
595 $q->td({-bgcolor=>'#bbbbff'},['HM Font Size:',
596 $q->textfield(-name=>'fontsize2',
600 $q->td({-bgcolor=>'#ddddff'},['HM Scale:',
601 $q->textfield(-name=>'hmscale',
605 $q->td({-bgcolor=>'#bbbbff'},['Display HM Angle:',
606 $q->checkbox(-name=>'disp_hm_angle',
610 $q->td({-bgcolor=>'#ddddff'},['Reverse Helix:',
611 $q->checkbox(-name=>'reverse_helix',
614 $q->td({-bgcolor=>'#bbbbff'},['No Color:',
615 $q->checkbox(-name=>'wo_color',
618 $q->td({-bgcolor=>'#ddddff'},['Hydrophobic Moment Displacement:',
619 $q->textfield(-name=>'hmdisp',
623 $q->td({-bgcolor=>'#bbbbff',-colspan=>2},
624 [$q->center($q->submit(-name=>'submit',-value=>'Submit'))
629 $q->hr,$q->i("Created by ".$q->a({-href=>"mailto:don\@donarmstrong.com"},'Don Armstrong')." and Raphael Zidovetzki. Version: ".$q->b($VERSION));