Annotation of loncom/cgi/plot.gif, revision 1.4
1.4 ! matthew 1: #!/usr/bin/perl -w
1.1 matthew 2: #
1.3 matthew 3: # $Id: plot.gif,v 1.2 2001/12/10 15:45:54 matthew Exp $
1.1 matthew 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: #
9: # LON-CAPA is free software; you can redistribute it and/or modify
10: # it under the terms of the GNU General Public License as published by
11: # the Free Software Foundation; either version 2 of the License, or
12: # (at your option) any later version.
13: #
14: # LON-CAPA is distributed in the hope that it will be useful,
15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: # GNU General Public License for more details.
18: #
19: # You should have received a copy of the GNU General Public License
20: # along with LON-CAPA; if not, write to the Free Software
21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/cgi-bin/plot.gif
24: #
25: # http://www.lon-capa.org/
26: #
1.2 matthew 27: ###########################################################################
28: #
1.1 matthew 29: # CGI-BIN interface to GD, used for making mathematical plots.
30: #
31: # User specifies the following variables (given are defaults):
32: # height = "100"
33: # width = "100"
34: # xmin = "-10.0"
35: # xmax = " 10.0"
36: # ymin = "-10.0"
37: # ymax = " 10.0"
1.2 matthew 38: # transparent (doesn't work with gif?)
1.1 matthew 39: # frame
40: # drawaxes
41: # drawtics
42: # vtic_every = "1.0"
43: # htic_every = "1.0"
44: # xseries1 = "x1,x2,x3,x4,x5,...,xn"
45: # yseries1 = "y1,y2,y3,y4,y5,...,yn"
46: # xseries2 = ..
47: # yseries2 = ..
48: # ...
49: # label1 = "x,y,size,text"
50: # label2 = "x,y,size,text"
51: # label3 = "x,y,size,text"
52: # ...
53: #
54: # size of a labelN is one of :
55: # giant, large, medium, small, tiny
56: #
1.2 matthew 57: ###########################################################################
1.4 ! matthew 58: ##
! 59: ## Data structures & file description
! 60: ##
! 61: ## The input file is taken to be comprised of "segments". Each "segment"
! 62: ## will hold data for the plot header, the coordinate axes, or (more likely)
! 63: ## the curves, circles, and polygons that are to be plotted.
! 64: ##
! 65: ## The global array @Segments holds references to hashes which contain the
! 66: ## data needed for each structure.
! 67: ##
! 68: use FileHandle;
1.1 matthew 69: use GD;
70:
1.4 ! matthew 71: my ($image,$axis);
! 72: $filename = shift;
! 73: # GET FILENAME AND OPEN THE FILE, BAIL OUT IF UNABLE TO DO SO
! 74: $fh = new FileHandle("<$filename");
! 75: my @Segments = &read_file($fh);
! 76:
! 77: foreach $segment (@Segments) {
! 78: &set_defaults($segment);
! 79: }
! 80: &init_image(&get_specific_segment(\@Segments,'plotheader'),
! 81: &get_specific_segment(\@Segments,'axis'));
! 82:
! 83: for (my $i =0; $i<=$#Segments; $i++) {
! 84: grok_segment($Segments[$i]);
1.1 matthew 85: }
1.4 ! matthew 86: &write_image();
1.1 matthew 87:
1.4 ! matthew 88: #---------------------------------------------------- convenience functions
! 89: sub write_image {
! 90: # Tell the browser our mime-type
! 91: # print <<END;
! 92: #Content-type: image/gif
! 93: #
! 94: #END
! 95: my $BinaryData=$image->png;
! 96: undef $image;
! 97: binmode(STDOUT);
! 98: open IMG,"|pngtopnm|ppmtogif 2>/dev/null"; # convert into a gif image
! 99: print IMG $BinaryData; # output image
! 100: $|=1; # be sure to flush before closing
! 101: close IMG;
! 102: }
1.1 matthew 103:
1.4 ! matthew 104: sub grok_segment {
! 105: $_ = shift;
! 106: my %Data = %$_;
! 107: $type = $Data{'type'};
! 108: if (!defined($type)) {
! 109: return undef;
! 110: } elsif ($type eq 'frame') {
! 111: draw_frame(\%Data);
! 112: } elsif ($type eq 'curve') {
! 113: draw_curve(\%Data);
! 114: } elsif ($type eq 'label') {
! 115: draw_label(\%Data);
! 116: } elsif ($type eq 'circle') {
! 117: draw_circle(\%Data);
! 118: } elsif ($type eq 'polygon') {
! 119: draw_polygon(\%Data);
! 120: } elsif ($type eq 'line') {
! 121: draw_line(\%Data);
! 122: }
! 123: }
1.1 matthew 124:
1.4 ! matthew 125: sub get_specific_segment {
! 126: $_ = shift;
! 127: my @Segments = @$_;
! 128: my $type = shift;
! 129: for ($i = 0; $i<=$#Segments; $i++) {
! 130: if ($Segments[$i]->{'type'} eq $type) {
! 131: return (splice @Segments, $i,1);
! 132: }
! 133: }
! 134: return undef;
! 135: }
1.1 matthew 136:
1.4 ! matthew 137: #---------------------------------------------------- plot description reading
! 138: sub read_file {
! 139: my @Returned_Segments;
! 140: my $fh = shift;
! 141: ($ret,$ref) = read_segment($fh);
! 142: while (defined($ret) && $ret !=0) {
! 143: push @Returned_Segments,$ref;
! 144: ($ret,$ref) = read_segment($fh);
! 145: }
! 146: return @Returned_Segments;
! 147: }
1.1 matthew 148:
1.4 ! matthew 149: sub newhash{
! 150: my %H;
! 151: return \%H;
! 152: }
1.1 matthew 153:
1.4 ! matthew 154: sub read_segment{
! 155: # Reads in a segment of a plotting file.
! 156: # Returns 1,\%Data on success (or parital success)
! 157: # Returns 0, undef on failure;
! 158: $fh = shift;
! 159: my $Data = newhash();
! 160:
! 161: $_ = <$fh>;
! 162: if (! /^NEW /) {
! 163: return undef;
! 164: }
1.1 matthew 165:
1.4 ! matthew 166: while($_=<$fh>) {
! 167: last if (/^END /);
! 168: # Lines are of the form "type::var=value", "NEW type", or "END type"
! 169: chomp;
! 170: return(0,undef) if (/^NEW /);
! 171: if (/(\w+)::(\w+)[\s]*=\s*\"([\w\s,\-\+\.]+)\"/) {
! 172: $Data->{'type'} = $1 if (!exists ($Data->{'type'}));
! 173: return(0,$Data) if ($Data->{'type'} ne $1);
! 174: $Data->{$2} = $3;
! 175: } else {
! 176: # Something went wrong - bad input - what to do?
! 177: }
1.1 matthew 178: }
1.4 ! matthew 179: return (1,$Data);
! 180: }
1.1 matthew 181:
1.4 ! matthew 182: #-------------------------------------------------------
! 183: sub init_image {
! 184: my $PlotHeader = shift;
! 185: $axis = shift;
! 186: # Take care of making the image
! 187: my ($width,$height) = ($PlotHeader->{'width'},$PlotHeader->{'height'});
! 188:
! 189: $image = new GD::Image($width,$height);
! 190: my $bgcolor = $image->colorAllocate(split/,/,$PlotHeader->{'bgcolor'});
! 191: my $fgcolor = $image->colorAllocate(split/,/,$PlotHeader->{'fgcolor'});
! 192: $image->transparent($bgcolor) if ($PlotHeader->{'transparent'} eq 'true');
! 193:
! 194: $axis->{'xlen'} = $axis->{'xmax'} - $axis->{'xmin'};
! 195: $axis->{'ylen'} = $axis->{'ymax'} - $axis->{'ymin'};
! 196: if ($axis->{'drawaxis'} eq 'true') {
! 197: &draw_axes();
! 198: }
! 199: if ($axis->{'drawtics'} eq 'true') {
! 200: &draw_tics();
! 201: }
1.1 matthew 202: }
203:
1.4 ! matthew 204: #-------------------------------------------------------- axis routines
! 205: sub draw_axes{
! 206: my $color = $image->colorResolve(split /,/,$axis->{'color'});
1.1 matthew 207: ($x1,$y1) = &transformPoint($axis->{"xmin"},0,$image,$axis);
208: ($x2,$y2) = &transformPoint($axis->{"xmax"},0,$image,$axis);
1.4 ! matthew 209: $image->line($x1,$y1,$x2,$y2,$color);
1.1 matthew 210: ($x1,$y1) = &transformPoint(0,$axis->{"ymin"},$image,$axis);
211: ($x2,$y2) = &transformPoint(0,$axis->{"ymax"},$image,$axis);
1.4 ! matthew 212: $image->line($x1,$y1,$x2,$y2,$color);
1.1 matthew 213: }
214:
1.4 ! matthew 215: sub draw_tics{
! 216: my $color = $image->colorResolve(split /,/, $axis->{'color'});
! 217: my ($htic_every,$vtic_every) = ($axis->{'htic_every'}, $axis->{'vtic_every'});
1.1 matthew 218: my ($width,$height) = $image->getBounds();
219:
1.4 ! matthew 220: my $ticwidth = ($width > 99 ? 5 : int($width /20) + 1);
! 221: my $ticheight = ($height > 99 ? 5 : int($height/20) + 1);
1.1 matthew 222:
223: # Do tics along y-axis
224: for ($ntic = 0; $ntic <=int($axis->{"ylen"}/$vtic_every); $ntic++){
225: my ($x1,$y1) = &transformPoint(0,$axis->{"ymin"}+$ntic*$vtic_every);
226: my ($x2,$y2) = &transformPoint(0,$axis->{"ymin"}+$ntic*$vtic_every);
227: $x1 -= $ticwidth;
228: $x2 += $ticwidth;
1.4 ! matthew 229: $image->line($x1,$y1,$x2,$y2,$color);
1.1 matthew 230: }
231: # Do tics along x-axis
232: for ($ntic = 0; $ntic <=int($axis->{"xlen"}/$htic_every); $ntic++){
233: my ($x1,$y1) = &transformPoint( $axis->{"xmin"}+$ntic*$htic_every,0);
234: my ($x2,$y2) = &transformPoint( $axis->{"xmin"}+$ntic*$htic_every,0);
235: $y1 -= $ticheight;
236: $y2 += $ticheight;
1.4 ! matthew 237: $image->line($x1,$y1,$x2,$y2,$color);
1.1 matthew 238: }
239: }
240:
1.4 ! matthew 241: #------------------------------------------------------- misc plotting routines
! 242: sub draw_frame {
! 243: my $Frame = shift;
! 244: my ($width,$height) = $image->getBounds();
! 245: my $color = $image->colorResolve(split /,/,$Frame->{'color'} );
! 246: # Draw a frame around the picture.
! 247: my $offset = $Frame->{'offset'};
! 248: for (my $i = 0; $i<=$Frame->{'thickness'}; $i++) {
! 249: $image->rectangle(
! 250: $offset - 1,
! 251: $offset - 1,
! 252: $width-$offset,
! 253: $height-$offset,
! 254: $color);
1.1 matthew 255: }
256: }
257:
1.4 ! matthew 258: sub draw_line{
! 259: my $Line = shift;
! 260: my $color = $image->colorResolve(split/,/, $Line->{'color'});
! 261: my ($x1,$y1) = &transformPoint($Line->{'x1'},$Line->{'y1'});
! 262: my ($x2,$y2) = &transformPoint($Line->{'x2'},$Line->{'y2'});
! 263: $image->line($x1,$y1,$x2,$y2,$color);
! 264: }
! 265:
! 266: sub draw_curve{
! 267: my $Curve = shift;
! 268: my $color = $image->colorResolve(split /,/, $Curve->{'color'});
! 269: @X = split /,/,$Curve->{'xdata'};
! 270: @Y = split /,/,$Curve->{'ydata'};
! 271: if ($#X != $#Y) {
! 272: return 0;
! 273: }
! 274: for($i=0;$i< $#X ;$i++) {
! 275: my ($x1,$y1) = &transformPoint($X[$i] ,$Y[$i]);
! 276: my ($x2,$y2) = &transformPoint($X[$i+1],$Y[$i+1]);
! 277: $image->line($x1,$y1,$x2,$y2,$color);
! 278: }
1.1 matthew 279: }
280:
1.4 ! matthew 281: sub draw_label{
! 282: my $Label = shift;
! 283: my $color = $image->colorResolve(split /,/, $Label->{'color'});
! 284: my $fontname = $Label->{'font'};
! 285: my $font = gdGiantFont if (lc($fontname) eq "giant" ||
! 286: lc($fontname) eq "huge" );
! 287: $font = gdLargeFont if (lc($fontname) eq "large" );
! 288: $font = gdMediumBoldFont if (lc($fontname) eq "medium");
! 289: $font = gdSmallFont if (lc($fontname) eq "small" );
! 290: $font = gdTinyFont if (lc($fontname) eq "tiny" );
! 291: my $text = $Label->{'text'};
1.1 matthew 292: if (! defined($font)) {
293: $font = gdGiantFont;
294: $text = "Font size error!";
295: }
1.4 ! matthew 296: my ($x,$y) = &transformPoint($Label->{'x'},$Label->{'y'});
1.1 matthew 297: $image->string($font,$x,$y,$text,$color);
298: }
299:
1.4 ! matthew 300: sub draw_circle {
! 301: my $Circle = shift;
! 302: my ($width,$height) = $image->getBounds();
! 303: my $color = $image->colorResolve(split /,/, $Circle->{'color'});
! 304: my ($x,$y) = &transformPoint(split/,/,$Circle->{'center'});
! 305: my $xradius = $Circle->{'radius'} * $width / $axis->{'xlen'};
! 306: my $yradius = $Circle->{'radius'} * $height / $axis->{'ylen'};
! 307: # draw a semicircle centered at 100,100
! 308: $image->arc($x,$y,$xradius,$yradius,0,360,$color);
! 309: $image->fill($x,$y,$color) if ($Circle->{'filled'} eq 'true');
! 310: }
1.1 matthew 311:
1.4 ! matthew 312: sub draw_polygon {
! 313: my $Poly = shift;
! 314: my $color = $image->colorResolve(split /,/, $Poly->{'color'});
! 315: @X = split /,/,$Poly->{'xdata'};
! 316: @Y = split /,/,$Poly->{'ydata'};
! 317: if ($#X != $#Y) {
! 318: return 0;
! 319: }
! 320: my $poly = new GD::Polygon;
! 321: for ($i=0;$i<=$#X;$i++) {
! 322: $poly->addPt(&transformPoint($X[$i],$Y[$i]));
! 323: }
! 324: if ($Poly->{'filled'} eq 'true') {
! 325: $image->filledPolygon($poly,$color);
! 326: } else {
! 327: $image->polygon($poly,$color);
! 328: }
! 329: }
1.1 matthew 330:
1.4 ! matthew 331: #------------------------------------------ transform point (basic routine)
! 332: #
! 333: # transformPoint(x,y) where x,y are in the coordinates of axis will return
! 334: # the coordinates transformed to the image coordinate system.
! 335: sub transformPoint{
! 336: my ($x,$y) = @_;
! 337: my ($width,$height) = $image->getBounds();
! 338: $x = ( $x - $axis->{"xmin"}) * $width / ( $axis->{"xlen"});
! 339: $y = ( ( $axis->{"ylen"} ) - ($y - $axis->{"ymin"}))
! 340: * $height / ( $axis->{"ylen"} );
! 341: return($x,$y);
! 342: }
1.1 matthew 343:
1.4 ! matthew 344: #------------------------------------------ set defaults is a beast!
1.1 matthew 345:
1.4 ! matthew 346: sub set_defaults {
! 347: my $PlotHeader = {
! 348: type => "plotheader",
! 349: name => "plot",
! 350: height => "200",
! 351: width => "300",
! 352: bgcolor => "255,255,255",
! 353: fgcolor => " 0, 0, 0",
! 354: transparent => "true"
! 355: };
! 356:
! 357: my $Axis = {
! 358: type => "axis",
! 359: name => "axis",
! 360: color => " 0, 0, 0",
! 361: drawtics => "true",
! 362: vtic_every => " 1.0",
! 363: htic_every => " 1.0",
! 364: xmin => "-10.0",
! 365: ymin => " -5.0",
! 366: xmax => " 10.0",
! 367: ymax => " 5.0",
! 368: drawaxis => "true"
! 369: };
! 370:
! 371: my $Frame = {
! 372: type => "frame",
! 373: color => " 0, 0, 0",
! 374: offset => "1.0",
! 375: thickness => "1.0",
! 376: drawframe => "true"
! 377: };
! 378:
! 379: my $Curve= {
! 380: type => "curve",
! 381: name => "curve",
! 382: color => " 0, 0, 0",
! 383: xdata => " 1.0, 2.0, 3.0, 4.0, 5.0, 6.0",
! 384: ydata => " 1.0, 2.0, 3.0, 4.0, 5.0, 6.0"
! 385: };
! 386:
! 387: my $Label = {
! 388: type => "label",
! 389: name => "label",
! 390: font => "medium",
! 391: text => "default label text",
! 392: color => " 0, 0, 0",
! 393: x => " -5.0",
! 394: y => " 5.0"
! 395: };
! 396:
! 397: my $Circle = {
! 398: type => "circle",
! 399: name => "circle",
! 400: color => " 0, 0, 0",
! 401: filled => "true",
! 402: center => "x,y",
! 403: radius => "12.0"
! 404: };
! 405:
! 406: my $Polygon = {
! 407: type => "polygon",
! 408: name => "polygon",
! 409: color => " 0, 0, 0",
! 410: filled => "true",
! 411: xdata => "1.0, 0.5, 0.0, -0,5, -1.0, -0.5, 0.0, 0.5",
! 412: ydata => "0.0,-0.5,-1.0, -0.5, 0.0, 0.5, 1.0, 0.5"
! 413: };
! 414:
! 415: my $Line = {
! 416: type => "line",
! 417: name => "line",
! 418: color => " 0, 0, 0",
! 419: x1 => "1.0",
! 420: y1 => "0.0",
! 421: x2 => "2.0",
! 422: y2 => "4.0"
! 423: };
! 424:
! 425: my $typematch = {
! 426: plotheader => $PlotHeader,
! 427: axis => $Axis,
! 428: frame => $Frame,
! 429: label => $Label,
! 430: curve => $Curve,
! 431: circle => $Circle,
! 432: polygon => $Polygon
! 433: };
! 434:
! 435: my $seg = shift;
! 436: if (exists($typematch->{$seg->{'type'}})) {
! 437: my $H = $typematch->{$seg->{'type'}};
! 438: foreach $key (keys %$H) {
! 439: if (! exists($seg->{$key})) {
! 440: $seg->{$key} = $H->{$key};
! 441: }
! 442: }
! 443: }
! 444: }
! 445:
1.1 matthew 446:
447:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>