#!/usr/bin/perl -w
##############################################
# Create a png image a user defined function
# on the fly with the GD.pm module.
#############################################
use strict;
use CGI;
my $func = CGI->new->param("f");
unless ($func) {
# --- print form asking user to define the function ---
print "Content-type: text/html\n\n";
print "
Enter a function
\n";
}
else {
# -- create and output png image showing plot of the function --
my $img = PlotFunction::makeImage($func);
print "Content-type: image/png\n\n";
binmode STDOUT; # needed for binary output on Windows
print $img->png;
}
# =====================================
{
package PlotFunction;
use GD;
use constant XPixBorder => 10;
use constant YPixBorder => 10;
use constant Epsilon => 1e-6;
use constant XMax => 8.0 + Epsilon;
use constant XMin => -8.0;
use constant YMax => 1.5 + Epsilon;
use constant YMin => -1.5;
use constant XPixSize => 384;
use constant YPixSize => 256;
use constant DX => (XMax - XMin)/(XPixSize/2);
use constant XConvert => XPixSize/(XMax - XMin);
use constant YConvert => YPixSize/(YMax - YMin);
our ($im,
$white, $black, $grey, $darkBlue,
$xpixpen, $ypixpen, $pencolor,
$f
);
our $initHasRun;
sub init {
return if $initHasRun;
$initHasRun=1;
# allocate new GD.pm Image.
our $im = GD::Image->new(XPixSize+2*XPixBorder, YPixSize+2*YPixBorder);
# allocate some colors. First allocated is background.
our $white = $im->colorAllocate(255,255,255);
our $black = $im->colorAllocate( 0, 0, 0);
our $grey = $im->colorAllocate(172,172,172);
our $darkBlue = $im->colorAllocate( 0, 0,128);
# make the background transparent and the image interlaced.
$im->transparent($white);
$im->interlaced('true');
}
# input: function f(x) to be plotted as a string, i.e. "sin(x)".
sub makeImage {
($f) = @_;
init(); # initialize globals
for ($f) { # convert function
s/exp/~~~/g; # from "exp(x)" form
s/x/\$x/g; # to "exp($x)"
s/~~~/exp/g;
}
$pencolor = $grey; # draw axes
pendown(XMin,0); lineto(XMax,0);
pendown(0,YMin); lineto(0,YMax);
$pencolor = $black; # draw function
my $x = XMin;
my $y = f($x);
my ($lastx, $lasty);
pendown($x,$y);
while ($x < XMax) {
$lastx = $x;
$lasty = $y;
$x += DX;
$y = f($x);
lineto($x,$y);
}
$im->string(gdLargeFont, # label plot
XPixBorder*2, YPixBorder*2,
"f(x) = $func", $darkBlue);
return $im;
}
# function to be plotted
sub f {
my ($x) = @_;
return eval $f;
}
# convert x coords to pix
sub xx {
my ($x) = @_;
return XPixBorder - (XMin-$x)*XConvert;
}
# convert x coords to pix
sub yy {
my ($y) = @_;
return YPixBorder + (YMax-$y)*YConvert;
}
# input: (x,y) coords to place pen
sub pendown {
my ($x,$y) = @_;
$xpixpen = xx($x);
$ypixpen = yy($y);
return;
}
# input: (x,y) coords
# draw line from last pendown position to (x,y)
sub lineto {
our $im;
my ($x, $y) = @_;
my ($xpixnew, $ypixnew) = ( xx($x), yy($y) );
$im->line($xpixpen, $ypixpen, $xpixnew, $ypixnew, $pencolor);
($xpixpen, $ypixpen) = ($xpixnew, $ypixnew);
return;
}
} # end PlotFunction package