<?php header("Content-type: image/png"); // Class Graph that generates a graphics image on the web. class Graph { var $width,$height,$marleft,$marright,$martop,$marbottom; var $xmin,$xmax,$ymin,$ymax; var $newxmin=0,$newxmaxi=0,$newymin=0,$newymax=0; var $scalex,$scaley; var $ndx,$ndy; var $image,$imageback,$imagefore; // Contructor for Graph, $width and $height are the width and height of the // total figure in in pixels, $marleft, $marright, $martop, $marbottom are the // widths of the left, right, top and bottom margins that bound the plotting // area. Any or all of them can be zero. No plots can be drawn outside the // margins, but axes and labels can be. $xmin, $xmax, $ymin and $ymax are the // mathematical limits that are mapped to pixels for the quantities plotted. // $ndx and $ndy are the number of big division for the scales, if one or the // other (or both are) is zero, the X or Y divisions are determined // automatically. $colorback and $colorfore are the background (default white) // and forground (default black) colors in RGB hexidecimal notation. function Graph($width, $height, $marleft, $marright, $martop, $marbottom, $xmin, $xmax, $ndx, $ymin, $ymax, $ndy, $colorback="#FFFFFF", $colorfore="#000000") { $this->width=$width; $this->height=$height; $this->marleft=$marleft; $this->marright=$width-1-$marright; $this->martop=$martop; $this->marbottom=$height-1-$marbottom; if ($ndx <= 0) { // Use automatic scaling of the X-axis with clscal(). $err=$this->clscal($xmin, $xmax, $newxmin, $dx, $newndx); if ($err == 0) { $ndx=$newndx; $xmin=$newxmin; $xmax=$xmin+$ndx*$dx; } else // Error condition - try our best and use 10 intervals. $ndx=10; } if ($ndy <= 0) { // Use automatic scaling of the Y-axis with clscal(). $err=$this->clscal($ymin, $ymax, $newymin, $dy, $newndy); if ($err == 0) { $ndy=$newndy; $ymin=$newymin; $ymax=$ymin+$ndy*$dy; } else // Error condition - try our best and use 10 intervals. $ndy=10; } $this->ndx = $ndx; $this->ndy = $ndy; $this->xmin = $xmin; $this->xmax = $xmax; $this->ymin = $ymin; $this->ymax = $ymax; $this->scalex=($this->marright-$marleft)/($xmax-$xmin); $this->scaley=($this->marbottom-$martop)/($ymax-$ymin); $this->image = imagecreate($width,$height); $this->imageback=imagecolorallocate($this->image, hexdec(substr($colorback,1,2)), hexdec(substr($colorback,3,2)), hexdec(substr($colorback,5,2))); $this->imagefore=imagecolorallocate($this->image, hexdec(substr($colorfore,1,2)), hexdec(substr($colorfore,3,2)), hexdec(substr($colorfore,5,2))); } // Displays the output picture. function display() { imagepng($this->image); } // Draws a box around the graph in the default color. function drawbox() { imagerectangle($this->image,0,0,$this->width-1,$this->height-1, $this->imagefore); } // Draw the outline of a rectangle, by default the color is the default // foreground color. function drawrect($left, $right, $top, $bottom, $colorfore="0") { if (strlen($colorfore) == 7) $imagefore=imagecolorallocate($this->image, hexdec(substr($colorfore,1,2)), hexdec(substr($colorfore,3,2)), hexdec(substr($colorfore,5,2))); else $imagefore=$this->imagefore; imagerectangle($this->image,$left,$top,$right,$bottom,$imagefore); } // Flood fill a rectangle with a particular color. If the color is the // background color, the default, this can be used to clear an area of a plot // where text will be written. function fillrect($left, $right, $top, $bottom, $colorback="0") { if (strlen($colorback) == 7) $imageback=imagecolorallocate($this->image, hexdec(substr($colorback,1,2)), hexdec(substr($colorback,3,2)), hexdec(substr($colorback,5,2))); else $imageback=$this->imageback; imagefilledrectangle($this->image,$left,$top,$right,$bottom,$imageback); } // Sets or resets the colors, on default no colors are changed. function setcolors($colorback="0", $colorfore="0") { if (strlen($colorback) == "7") $this->imageback=imagecolorallocate($this->image, hexdec(substr($colorback,1,2)), hexdec(substr($colorback,3,2)), hexdec(substr($colorback,5,2))); if (strlen($colorfore) == "7") $this->imagefore=imagecolorallocate($this->image, hexdec(substr($colorfore,1,2)), hexdec(substr($colorfore,3,2)), hexdec(substr($colorfore,5,2))); } // Joins up $n values of X and Y held in the arrays $x and $y in the plotting // area for a given color, which by default is the current foreground color. function plotcurve($x, $y, $n, $colorfore="0") { if (strlen($colorfore) == 7) $this->imagefore= imagecolorallocate($this->image,hexdec(substr($colorfore,1,2)), hexdec(substr($colorfore, 3, 2)),hexdec(substr($colorfore,5,2))); for ($i=0; $i<=$n; $i++) { $xp=($x[$i]-$this->xmin)*$this->scalex+$this->marleft; $yp=$this->marbottom+/*$this->martop*/- ($y[$i]-$this->ymin)*$this->scaley; if ($i > 0) { if ($xp >= $this->marleft && $xp <= $this->marright && $xpo >= $this->marleft && $xpo <= $this->marright && $yp >= $this->martop && $yp <= $this->marbottom && $ypo >= $this->martop && $ypo <= $this->marbottom) imageline($this->image,$xpo,$ypo,$xp,$yp,$this->imagefore); } $xpo=$xp; $ypo=$yp; } } // Draws a title at the top. function drawtitle($title, $font=5) { $xp=($this->width-imagefontwidth($font)*strlen($title))/2; $yp=0; imagestring($this->image,$font,$xp,$yp,$title,$this->imagefore); } // Labels the X-axis. function drawxtitle($xtitle, $font=4) { $xp=($this->width-imagefontwidth($font)*strlen($xtitle))/2; $yp=$this->height-15; imagestring($this->image,$font,$xp,$yp,$xtitle,$this->imagefore); } // Labels the Y-axis. function drawytitle($ytitle, $font=4) { $xp=0; $yp=($this->height+imagefontwidth($font)*strlen($ytitle))/2; imagestringup($this->image,$font,$xp,$yp,$ytitle,$this->imagefore); } // Places some text anywhere on the plot at position xp and yp horzontally if // $horizonal=TRUE, the default, otherwise vertically. If $pixelxy=TRUE, the // default, xp and yp are thew coordinates in pixels where the text is started, // otherwise they are in mathematical coordinates. function drawtext($text, $xp, $yp, $horizonal=TRUE, $pixelxy=TRUE,$font=1) { if (!$pixelxy) { $xp=($xp-$this->xmin)*$this->scalex; $yp=$this->height-1-($yp-$this->ymin)*$this->scaley; } if ($horizonal) imagestring($this->image,$font,$xp,$yp,$text,$this->imagefore); else imagestringup($this->image,$font,$xp,$yp,$text,$this->imagefore); } // Draw ticks in the X-direction and possibly an axis. If $up is even the // ticks will be drawn up, otherwise they will be drawn down. $yoff is the // Y-offset of the X-axis, if $up<=1 then it is the offset in pixels up from the // bottom if $up=0 or down from the top if $up=1. If $up>1 then it is the // position in mathematical units, i.e. if $off=0, it is the true X-axis. // $line=TRUE to draw an axis, otherwise none is drawn, and $btk, $mtk and $stk // are respectively the size in pixels for the big, medium and small tick marks. function drawxticks($up=0, $upmark=0, $yoff=0, $line=TRUE, $font=0, $fontoff=9, $btk=10, $mtk=6, $stk=3) { $npx=($this->xmax-$this->xmin)/$this->ndx*$this->scalex; // Big tick. $nsxunits=($this->marright-$this->marleft)/$npx*10; // # small ticks. if ($up >= 2) $yp=$this->marbottom-($yoff-$this->ymin)*$this->scaley;//Y-position. else { if ($up%2 == 0) $yp=$this->height-1-$yoff; else $yp=$yoff; } if ((2*($up%2)-1)*(2*($upmark%2)-1) < 0) { $btk=-$btk; $mtk=-$mtk; $stk=-$stk; } if ($font > 0) { // Adjust Y-position of the text if required. $fontoff+=imagefontheight($font)/2; if ((2*($up%2)-1)*(2*(intval($upmark/2)%2)-1) < 0) $fontoff=-$fontoff; if (($up%2+$upmark)%2 == 0) $fontoff+=8; // Fine adjustment. $fontwidth=imagefontwidth($font); } for ($i=0; $i<=$nsxunits; $i++) { $xpnum=$npx*$i*0.1; $xp=$xpnum+$this->marleft; if ($i%10 == 0) { // Draw a big tick for 1 unit and an optional num. imageline($this->image,$xp,$yp,$xp,$yp-$btk,$this->imagefore); if ($font > 0) { $num=$xpnum/$this->scalex+$this->xmin; $strnum=strval(round($num,5)); $textwidth=$fontwidth*strlen($strnum); $xfontoff=$textwidth/2; if ($xp-$xfontoff > $this->marleft && $xp+$xfontoff < $this->marright) imagestring($this->image,$font,$xp-$xfontoff, $yp-$fontoff,$strnum,$this->imagefore); } } else if (($i+5)%10 == 0) // Draw a medium tick for 1/2 unit. imageline($this->image,$xp,$yp,$xp,$yp-$mtk,$this->imagefore); else // Draw a small tick for 1/10 unit. imageline($this->image,$xp,$yp,$xp,$yp-$stk,$this->imagefore); } if ($line) // Draw a horizontal line as an axis. imageline($this->image,$this->marleft,$yp,$this->marright,$yp, $this->imagefore); } // Draw ticks in the Y-direction and possibly an axis. If $left is even the // ticks will be drawn from the left to the right, otherwise they will be drawn // from the right to the left. $xoff is the X-offset of the Y-axis, if // $right<=1 then it is the offset in pixels from the left side if $right=0, or // the offset in pixels from the right side if $right=1. If $right>1 then it is // the position in mathematical units, i.e. if $off=0, it is the true Y-axis. // $line=TRUE to draw an axis, otherwise none is drawn, and $btk, $mtk and $stk // are respectively the size in pixels for the big, medium and small tick marks. function drawyticks($right=0, $rightmark=0, $xoff=0, $line=TRUE, $font=0, $fontoff=9, $btk=10, $mtk=6, $stk=3) { $npy=($this->ymax-$this->ymin)/$this->ndy*$this->scaley; // Big tick. $nsyunits=($this->marbottom-$this->martop)/$npy*10; // # small ticks. if ($right >= 2) $xp=($xoff-$this->xmin)*$this->scalex; // X-position. else { if ($right%2 == 0) $xp=$xoff; else $xp=$this->width-1-$xoff; } if ((2*($right%2)-1)*(2*($rightmark%2)-1) < 0) { $btk=-$btk; $mtk=-$mtk; $stk=-$stk; } if ($font > 0) { // Adjust X-position of the text if required. $fontoff+=imagefontheight($font); if ((2*($right%2)-1)*(2*(intval($rightmark/2)%2)-1) < 0) $fontoff=-$fontoff; if (($right%2+$rightmark)%2 == 0) $fontoff+=5;// Fine adjustment. if ($right%2 == 0 && $rightmark < 2 || $right%2 == 1 && $rightmark >=2) $fontoff-=15; $fontwidth=imagefontwidth($font); } for ($i=0; $i<=$nsyunits; $i++) { $ypnum=$npy*$i*0.1; $yp=$this->marbottom-$ypnum; if ($i%10 == 0) { // Draw a big tick for a unit and an option num. imageline($this->image,$xp,$yp,$xp+$btk,$yp,$this->imagefore); if ($font > 0) { $num=$ypnum/$this->scaley+$this->ymin; $strnum=strval(round($num,5)); $textwidth=$fontwidth*strlen($strnum); $yfontoff=$textwidth/2; if ($yp-$yfontoff > $this->martop && $yp+$yfontoff < $this->marbottom) imagestringup($this->image,$font,$xp+$fontoff, $yp+$yfontoff,$strnum,$this->imagefore); } } else if (($i+5)%10 == 0) // Draw a medium tick for 1/2 unit. imageline($this->image,$xp,$yp,$xp+$mtk,$yp,$this->imagefore); else // Draw a small tick for 1/10 unit. imageline($this->image,$xp,$yp,$xp+$stk,$yp,$this->imagefore); } if ($line) // Draw a vertical line as an axis. imageline($this->image,$xp,$this->martop,$xp,$this->marbottom, $this->imagefore); } // Given values to be plotted between $a and $b, this returns a lower bound $x, // such that $x < $a, the interval size $dx and the number of intervals $ndx, // with an upper bound $x+$ndx*$dx, such that $x+$ndx*$dx >= $b. The bounds // and the intervals are chosen to be "nice" values. The function also returns // with the following error codes: // 0 - Successful with the returned values in $x, $dx and $ndx. // 1 - $a >= $b. // 2 - $a = $b to four significant figures. // 3 - $ndx could not be chosen while keeping $dx to a valid value. // On failure clscal return with $x = $a, $dx = $b and $ndx = 1. function clscal($a, $b, &$x, &$dx, &$ndx) { $ivs = array(1,2,5,10,20,50,100,200,500,1000,2000,5000); $npref = array(5,6,4,7,8,3,2,9,10,1); $x=$a; $dx=$b; $ndx; if ($b <= $a) return 1; // ERROR - $a <= $b. $l=0; if ($a >= 0.0) $l=1; if ($b <= 0.0) $l=2; if ($l > 0) { if ($l == 2) { $temp=$a; $a=-$b; $b=-$temp; } $nb=0; while ($b < 1000.0) { $nb--; $b*=10.0; } while ($b >= 10000.0) { $nb++; $b*=0.1; } $mb=intval($b-0.1); $mb++; $a=$a*pow(10,-$nb); $ma=intval($a+0.01); if ($ma >= $mb) return 2; // ERROR - $a == $b to 4 sig. figures. for ($i=0; $i<12; $i++) { $n2=intval($mb/$ivs[$i]); if ($n2*$ivs[$i] != $mb) $n2++; $n1=intval($ma/$ivs[$i]); if ($n1*10 < $n2) $n1=0; $dxs[$i]=$ivs[$i]*pow(10,$nb); $ndxs[$i]=$n2-$n1; $xs[$i]=$n1*$dxs[$i]; if ($l == 2) $xs[$i]=-$n2*$dxs[$i]; } } else { if (-$a < $b) { $m=2; $xx=$b; $yy=-$a; } else { $m=1; $xx=-$a; $yy=$b; } $nx=0; while ($xx < 1000.0) { $nx--; $xx*=10.0; } while ($xx >= 10000.0) { $nx++; $xx*=0.1; } $mx=intval($xx-0.01); $mx++; for ($i=0; $i<12; $i++) { $n1=intval($mx/$ivs[$i]); if ($n1*$ivs[$i] != $mx) $n1++; $dxs[$i]=$ivs[$i]*pow(10,$nx); $n2=intval($yy/$dxs[$i]); if ($n2*$dxs[$i] < $yy) $n2++; $ndxs[$i]=$n1+$n2; $xs[$i]=-$n1*$dxs[$i]; if ($m == 2) $xs[$i]=-$n2*$dxs[$i]; } } for ($i=0; $i<10; $i++) for ($j=0; $j<12; $j++) { if ($ndxs[$j] == $npref[$i]) { $x=$xs[$j]; $dx=$dxs[$j]; $ndx=$ndxs[$j]; return 0; // SUCCESS - Suitable values of $x, $dx & $ndx. } } return 3; // ERROR - Unable to find suitable values of $x, $dx & $ndx. } } ?>