Subversion Repositories Applications.papyrus

Compare Revisions

Ignore whitespace Rev 2149 → Rev 2150

/trunk/api/jpgraph_1.12.2/jpgraph_pie3d.php
New file
0,0 → 1,883
<?php
/*=======================================================================
// File: JPGRAPH_PIE3D.PHP
// Description: 3D Pie plot extension for JpGraph
// Created: 2001-03-24
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_pie3d.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
//===================================================
// CLASS PiePlot3D
// Description: Plots a 3D pie with a specified projection
// angle between 20 and 70 degrees.
//===================================================
class PiePlot3D extends PiePlot {
var $labelhintcolor="red",$showlabelhint=true,$labelmargin=0.30;
var $angle=50;
var $edgecolor="", $edgeweight=1;
var $iThickness=false;
//---------------
// CONSTRUCTOR
function PiePlot3d(&$data) {
$this->radius = 0.5;
$this->data = $data;
$this->title = new Text("");
$this->title->SetFont(FF_FONT1,FS_BOLD);
$this->value = new DisplayValue();
$this->value->Show();
$this->value->SetFormat('%.0f%%');
}
 
//---------------
// PUBLIC METHODS
// Set label arrays
function SetLegends($aLegend) {
$this->legends = array_reverse($aLegend);
}
 
function SetSliceColors($aColors) {
$this->setslicecolors = $aColors;
}
 
function Legend(&$aGraph) {
parent::Legend($aGraph);
$aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
}
 
function SetCSIMTargets($targets,$alts=null) {
$this->csimtargets = $targets;
$this->csimalts = $alts;
}
 
// Should the slices be separated by a line? If color is specified as "" no line
// will be used to separate pie slices.
function SetEdge($aColor,$aWeight=1) {
$this->edgecolor = $aColor;
$this->edgeweight = $aWeight;
}
 
// Specify projection angle for 3D in degrees
// Must be between 20 and 70 degrees
function SetAngle($a) {
if( $a<5 || $a>90 )
JpGraphError::Raise("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees.");
else
$this->angle = $a;
}
 
function AddSliceToCSIM($i,$xc,$yc,$height,$width,$thick,$sa,$ea) { //Slice number, ellipse centre (x,y), height, width, start angle, end angle
 
$sa *= M_PI/180;
$ea *= M_PI/180;
 
//add coordinates of the centre to the map
$coords = "$xc, $yc";
 
//add coordinates of the first point on the arc to the map
$xp = floor($width*cos($sa)/2+$xc);
$yp = floor($yc-$height*sin($sa)/2);
$coords.= ", $xp, $yp";
 
//If on the front half, add the thickness offset
if ($sa >= M_PI && $sa <= 2*M_PI*1.01) {
$yp = floor($yp+$thick);
$coords.= ", $xp, $yp";
}
//add coordinates every 0.2 radians
$a=$sa+0.2;
while ($a<$ea) {
$xp = floor($width*cos($a)/2+$xc);
if ($a >= M_PI && $a <= 2*M_PI*1.01) {
$yp = floor($yc-($height*sin($a)/2)+$thick);
} else {
$yp = floor($yc-$height*sin($a)/2);
}
$coords.= ", $xp, $yp";
$a += 0.2;
}
//Add the last point on the arc
$xp = floor($width*cos($ea)/2+$xc);
$yp = floor($yc-$height*sin($ea)/2);
 
 
if ($ea >= M_PI && $ea <= 2*M_PI*1.01) {
$coords.= ", $xp, ".floor($yp+$thick);
}
$coords.= ", $xp, $yp";
$alt='';
if( !empty($this->csimalts[$i]) ) {
$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
$alt="alt=\"$tmp\" title=\"$tmp\"";
}
if( !empty($this->csimtargets[$i]) )
$this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\" $alt>\n";
}
 
function SetLabels($aLabels,$aLblPosAdj="auto") {
$this->labels = $aLabels;
$this->ilabelposadj=$aLblPosAdj;
}
 
// Distance from the pie to the labels
function SetLabelMargin($m) {
assert($m>0 && $m<1);
$this->labelmargin=$m;
}
// Show a thin line from the pie to the label for a specific slice
function ShowLabelHint($f=true) {
$this->showlabelhint=$f;
}
// Set color of hint line to label for each slice
function SetLabelHintColor($c) {
$this->labelhintcolor=$c;
}
 
function SetHeight($aHeight) {
$this->iThickness = $aHeight;
}
 
 
// Normalize Angle between 0-360
function NormAngle($a) {
// Normalize anle to 0 to 2M_PI
//
if( $a > 0 ) {
while($a > 360) $a -= 360;
}
else {
while($a < 0) $a += 360;
}
if( $a < 0 )
$a = 360 + $a;
 
if( $a == 360 ) $a=0;
return $a;
}
 
 
// Draw one 3D pie slice at position ($xc,$yc) with height $z
function Pie3DSlice($img,$xc,$yc,$w,$h,$sa,$ea,$z,$fillcolor,$shadow=0.65) {
// Due to the way the 3D Pie algorithm works we are
// guaranteed that any slice we get into this method
// belongs to either the left or right side of the
// pie ellipse. Hence, no slice will cross 90 or 270
// point.
if( ($sa < 90 && $ea > 90) || ( ($sa > 90 && $sa < 270) && $ea > 270) ) {
JpGraphError::Raise('Internal assertion failed. Pie3D::Pie3DSlice');
exit(1);
}
 
$p[] = array();
 
// Setup pre-calculated values
$rsa = $sa/180*M_PI; // to Rad
$rea = $ea/180*M_PI; // to Rad
$sinsa = sin($rsa);
$cossa = cos($rsa);
$sinea = sin($rea);
$cosea = cos($rea);
 
// p[] is the points for the overall slice and
// pt[] is the points for the top pie
 
// Angular step when approximating the arc with a polygon train.
$step = 0.05;
 
if( $sa >= 270 ) {
if( $ea > 360 || ($ea > 0 && $ea <= 90) ) {
if( $ea > 0 && $ea <= 90 ) {
// Adjust angle to simplify conditions in loops
$rea += 2*M_PI;
}
 
$p = array($xc,$yc,$xc,$yc+$z,
$xc+$w*$cossa,$z+$yc-$h*$sinsa);
$pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
 
for( $a=$rsa; $a < 2*M_PI; $a += $step ) {
$tca = cos($a);
$tsa = sin($a);
$p[] = $xc+$w*$tca;
$p[] = $z+$yc-$h*$tsa;
$pt[] = $xc+$w*$tca;
$pt[] = $yc-$h*$tsa;
}
 
$pt[] = $xc+$w;
$pt[] = $yc;
 
$p[] = $xc+$w;
$p[] = $z+$yc;
$p[] = $xc+$w;
$p[] = $yc;
$p[] = $xc;
$p[] = $yc;
 
for( $a=2*M_PI+$step; $a < $rea; $a += $step ) {
$pt[] = $xc + $w*cos($a);
$pt[] = $yc - $h*sin($a);
}
$pt[] = $xc+$w*$cosea;
$pt[] = $yc-$h*$sinea;
$pt[] = $xc;
$pt[] = $yc;
 
}
else {
$p = array($xc,$yc,$xc,$yc+$z,
$xc+$w*$cossa,$z+$yc-$h*$sinsa);
$pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
$rea = $rea == 0.0 ? 2*M_PI : $rea;
for( $a=$rsa; $a < $rea; $a += $step ) {
$tca = cos($a);
$tsa = sin($a);
$p[] = $xc+$w*$tca;
$p[] = $z+$yc-$h*$tsa;
$pt[] = $xc+$w*$tca;
$pt[] = $yc-$h*$tsa;
}
 
$pt[] = $xc+$w*$cosea;
$pt[] = $yc-$h*$sinea;
$pt[] = $xc;
$pt[] = $yc;
$p[] = $xc+$w*$cosea;
$p[] = $z+$yc-$h*$sinea;
$p[] = $xc+$w*$cosea;
$p[] = $yc-$h*$sinea;
$p[] = $xc;
$p[] = $yc;
}
}
elseif( $sa >= 180 ) {
$p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
$pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
for( $a=$rea; $a>$rsa; $a -= $step ) {
$tca = cos($a);
$tsa = sin($a);
$p[] = $xc+$w*$tca;
$p[] = $z+$yc-$h*$tsa;
$pt[] = $xc+$w*$tca;
$pt[] = $yc-$h*$tsa;
}
 
$pt[] = $xc+$w*$cossa;
$pt[] = $yc-$h*$sinsa;
$pt[] = $xc;
$pt[] = $yc;
$p[] = $xc+$w*$cossa;
$p[] = $z+$yc-$h*$sinsa;
$p[] = $xc+$w*$cossa;
$p[] = $yc-$h*$sinsa;
$p[] = $xc;
$p[] = $yc;
}
elseif( $sa >= 90 ) {
if( $ea > 180 ) {
$p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
$pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
 
for( $a=$rea; $a > M_PI; $a -= $step ) {
$tca = cos($a);
$tsa = sin($a);
$p[] = $xc+$w*$tca;
$p[] = $z + $yc - $h*$tsa;
$pt[] = $xc+$w*$tca;
$pt[] = $yc-$h*$tsa;
}
 
$p[] = $xc-$w;
$p[] = $z+$yc;
$p[] = $xc-$w;
$p[] = $yc;
$p[] = $xc;
$p[] = $yc;
 
$pt[] = $xc-$w;
$pt[] = $z+$yc;
$pt[] = $xc-$w;
$pt[] = $yc;
 
for( $a=M_PI-$step; $a > $rsa; $a -= $step ) {
$pt[] = $xc + $w*cos($a);
$pt[] = $yc - $h*sin($a);
}
 
$pt[] = $xc+$w*$cossa;
$pt[] = $yc-$h*$sinsa;
$pt[] = $xc;
$pt[] = $yc;
 
}
else { // $sa >= 90 && $ea <= 180
$p = array($xc,$yc,$xc,$yc+$z,
$xc+$w*$cosea,$z+$yc-$h*$sinea,
$xc+$w*$cosea,$yc-$h*$sinea,
$xc,$yc);
 
$pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
 
for( $a=$rea; $a>$rsa; $a -= $step ) {
$pt[] = $xc + $w*cos($a);
$pt[] = $yc - $h*sin($a);
}
 
$pt[] = $xc+$w*$cossa;
$pt[] = $yc-$h*$sinsa;
$pt[] = $xc;
$pt[] = $yc;
 
}
}
else { // sa > 0 && ea < 90
 
$p = array($xc,$yc,$xc,$yc+$z,
$xc+$w*$cossa,$z+$yc-$h*$sinsa,
$xc+$w*$cossa,$yc-$h*$sinsa,
$xc,$yc);
 
$pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
 
for( $a=$rsa; $a < $rea; $a += $step ) {
$pt[] = $xc + $w*cos($a);
$pt[] = $yc - $h*sin($a);
}
 
$pt[] = $xc+$w*$cosea;
$pt[] = $yc-$h*$sinea;
$pt[] = $xc;
$pt[] = $yc;
}
$img->PushColor($fillcolor.":".$shadow);
$img->FilledPolygon($p);
$img->PopColor();
 
$img->PushColor($fillcolor);
$img->FilledPolygon($pt);
$img->PopColor();
}
// Draw a 3D Pie
function Pie3D($aaoption,$img,$data,$colors,$xc,$yc,$d,$angle,$z,
$shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) {
 
//---------------------------------------------------------------------------
// As usual the algorithm get more complicated than I originally
// envisioned. I believe that this is as simple as it is possible
// to do it with the features I want. It's a good exercise to start
// thinking on how to do this to convince your self that all this
// is really needed for the general case.
//
// The algorithm two draw 3D pies without "real 3D" is done in
// two steps.
// First imagine the pie cut in half through a thought line between
// 12'a clock and 6'a clock. It now easy to imagine that we can plot
// the individual slices for each half by starting with the topmost
// pie slice and continue down to 6'a clock.
//
// In the algortithm this is done in three principal steps
// Step 1. Do the knife cut to ensure by splitting slices that extends
// over the cut line. This is done by splitting the original slices into
// upto 3 subslices.
// Step 2. Find the top slice for each half
// Step 3. Draw the slices from top to bottom
//
// The thing that slightly complicates this scheme with all the
// angle comparisons below is that we can have an arbitrary start
// angle so we must take into account the different equivalence classes.
// For the same reason we must walk through the angle array in a
// modulo fashion.
//
// Limitations of algorithm:
// * A small exploded slice which crosses the 270 degree point
// will get slightly nagged close to the center due to the fact that
// we print the slices in Z-order and that the slice left part
// get printed first and might get slightly nagged by a larger
// slice on the right side just before the right part of the small
// slice. Not a major problem though.
//---------------------------------------------------------------------------
 
// Determine the height of the ellippse which gives an
// indication of the inclination angle
$h = ($angle/90.0)*$d;
$sum = 0;
for($i=0; $i<count($data); ++$i ) {
$sum += $data[$i];
}
// Special optimization
if( $sum==0 ) return;
 
// Setup the start
$accsum = 0;
$a = $startangle;
$a = $this->NormAngle($a);
 
//
// Step 1 . Split all slices that crosses 90 or 270
//
$idx=0;
$adjexplode=array();
$numcolors = count($colors);
for($i=0; $i<count($data); ++$i, ++$idx ) {
$da = $data[$i]/$sum * 360;
 
if( empty($this->explode_radius[$i]) )
$this->explode_radius[$i]=0;
 
$expscale=1;
if( $aaoption == 1 )
$expscale=2;
 
$la = $a + $da/2;
$explode = array( $xc + $this->explode_radius[$i]*cos($la*M_PI/180)*$expscale,
$yc - $this->explode_radius[$i]*sin($la*M_PI/180) * ($h/$d) *$expscale );
$adjexplode[$idx] = $explode;
$labeldata[$i] = array($la,$explode[0],$explode[1]);
$originalangles[$i] = array($a,$a+$da);
 
$ne = $this->NormAngle($a+$da);
if( $da <= 180 ) {
// If the slice size is <= 90 it can at maximum cut across
// one boundary (either 90 or 270) where it needs to be split
$split=-1; // no split
if( ($da<=90 && ($a <= 90 && $ne > 90)) ||
(($da <= 180 && $da >90) && (($a < 90 || $a >= 270) && $ne > 90)) ) {
$split = 90;
}
elseif( ($da<=90 && ($a <= 270 && $ne > 270)) ||
(($da<=180 && $da>90) && ($a >= 90 && $a < 270 && ($a+$da) > 270 )) ) {
$split = 270;
}
if( $split > 0 ) { // split in two
$angles[$idx] = array($a,$split);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
$angles[++$idx] = array($split,$ne);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
}
else { // no split
$angles[$idx] = array($a,$ne);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
}
}
else {
// da>180
// Slice may, depending on position, cross one or two
// bonudaries
 
if( $a < 90 )
$split = 90;
elseif( $a <= 270 )
$split = 270;
else
$split = 90;
 
$angles[$idx] = array($a,$split);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
//if( $a+$da > 360-$split ) {
// For slices larger than 270 degrees we might cross
// another boundary as well. This means that we must
// split the slice further. The comparison gets a little
// bit complicated since we must take into accound that
// a pie might have a startangle >0 and hence a slice might
// wrap around the 0 angle.
// Three cases:
// a) Slice starts before 90 and hence gets a split=90, but
// we must also check if we need to split at 270
// b) Slice starts after 90 but before 270 and slices
// crosses 90 (after a wrap around of 0)
// c) If start is > 270 (hence the firstr split is at 90)
// and the slice is so large that it goes all the way
// around 270.
if( ($a < 90 && ($a+$da > 270)) ||
($a > 90 && $a<=270 && ($a+$da>360+90) ) ||
($a > 270 && $this->NormAngle($a+$da)>270) ) {
$angles[++$idx] = array($split,360-$split);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
$angles[++$idx] = array(360-$split,$ne);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
}
else {
// Just a simple split to the previous decided
// angle.
$angles[++$idx] = array($split,$ne);
$adjcolors[$idx] = $colors[$i % $numcolors];
$adjexplode[$idx] = $explode;
}
}
$a += $da;
$a = $this->NormAngle($a);
}
 
// Total number of slices
$n = count($angles);
 
for($i=0; $i<$n; ++$i) {
list($dbgs,$dbge) = $angles[$i];
}
 
//
// Step 2. Find start index (first pie that starts in upper left quadrant)
//
$minval = $angles[0][0];
$min = 0;
for( $i=0; $i<$n; ++$i ) {
if( $angles[$i][0] < $minval ) {
$minval = $angles[$i][0];
$min = $i;
}
}
$j = $min;
$cnt = 0;
while( $angles[$j][1] <= 90 ) {
$j++;
if( $j>=$n) {
$j=0;
}
if( $cnt > $n ) {
JpGraphError::Raise("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
}
++$cnt;
}
$start = $j;
 
//
// Step 3. Print slices in z-order
//
$cnt = 0;
// First stroke all the slices between 90 and 270 (left half circle)
// counterclockwise
while( $angles[$j][0] < 270 && $aaoption !== 2 ) {
 
list($x,$y) = $adjexplode[$j];
 
$this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
$z,$adjcolors[$j],$shadow);
$last = array($x,$y,$j);
 
$j++;
if( $j >= $n ) $j=0;
if( $cnt > $n ) {
JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
}
++$cnt;
}
$slice_left = $n-$cnt;
$j=$start-1;
if($j<0) $j=$n-1;
$cnt = 0;
// The stroke all slices from 90 to -90 (right half circle)
// clockwise
while( $cnt < $slice_left && $aaoption !== 2 ) {
 
list($x,$y) = $adjexplode[$j];
 
$this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
$z,$adjcolors[$j],$shadow);
$j--;
if( $cnt > $n ) {
JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
}
if($j<0) $j=$n-1;
$cnt++;
}
// Now do a special thing. Stroke the last slice on the left
// halfcircle one more time. This is needed in the case where
// the slice close to 270 have been exploded. In that case the
// part of the slice close to the center of the pie might be
// slightly nagged.
if( $aaoption !== 2 )
$this->Pie3DSlice($img,$last[0],$last[1],$d,$h,$angles[$last[2]][0],
$angles[$last[2]][1],$z,$adjcolors[$last[2]],$shadow);
 
 
if( $aaoption !== 1 ) {
// Now print possible labels and add csim
$img->SetFont($this->value->ff,$this->value->fs);
$margin = $img->GetFontHeight()/2;
for($i=0; $i < count($data); ++$i ) {
$la = $labeldata[$i][0];
$x = $labeldata[$i][1] + cos($la*M_PI/180)*($d+$margin);
$y = $labeldata[$i][2] - sin($la*M_PI/180)*($h+$margin);
if( $la > 180 && $la < 360 ) $y += $z;
if( $this->labeltype == 0 )
if( $sum > 0 )
$l = 100*$data[$i]/$sum;
else
$l = 0;
else
$l = $data[$i];
if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
$l=sprintf($this->labels[$i],$l);
 
$this->StrokeLabels($l,$img,$labeldata[$i][0]*M_PI/180,$x,$y,$z);
$this->AddSliceToCSIM($i,$labeldata[$i][1],$labeldata[$i][2],$h*2,$d*2,$z,
$originalangles[$i][0],$originalangles[$i][1]);
}
}
 
//
// Finally add potential lines in pie
//
 
if( $edgecolor=="" || $aaoption !== 0 ) return;
 
$accsum = 0;
$a = $startangle;
$a = $this->NormAngle($a);
 
$a *= M_PI/180.0;
 
$idx=0;
$img->PushColor($edgecolor);
$img->SetLineWeight($edgeweight);
$fulledge = true;
for($i=0; $i < count($data) && $fulledge; ++$i ) {
if( empty($this->explode_radius[$i]) )
$this->explode_radius[$i]=0;
if( $this->explode_radius[$i] > 0 ) {
$fulledge = false;
}
}
 
for($i=0; $i < count($data); ++$i, ++$idx ) {
 
$da = $data[$i]/$sum * 2*M_PI;
$this->StrokeFullSliceFrame($img,$xc,$yc,$a,$a+$da,$d,$h,$z,$edgecolor,
$this->explode_radius[$i],$fulledge);
$a += $da;
}
$img->PopColor();
}
 
function StrokeFullSliceFrame($img,$xc,$yc,$sa,$ea,$w,$h,$z,$edgecolor,$exploderadius,$fulledge) {
$step = 0.02;
 
if( $exploderadius > 0 ) {
$la = ($sa+$ea)/2;
$xc += $exploderadius*cos($la);
$yc -= $exploderadius*sin($la) * ($h/$w) ;
}
 
$p = array($xc,$yc,$xc+$w*cos($sa),$yc-$h*sin($sa));
 
for($a=$sa; $a < $ea; $a += $step ) {
$p[] = $xc + $w*cos($a);
$p[] = $yc - $h*sin($a);
}
 
$p[] = $xc+$w*cos($ea);
$p[] = $yc-$h*sin($ea);
$p[] = $xc;
$p[] = $yc;
 
$img->SetColor($edgecolor);
$img->Polygon($p);
 
// Unfortunately we can't really draw the full edge around the whole of
// of the slice if any of the slices are exploded. The reason is that
// this algorithm is to simply. There are cases where the edges will
// "overwrite" other slices when they have been exploded.
// Doing the full, proper 3D hidden lines stiff is actually quite
// tricky. So for exploded pies we only draw the top edge. Not perfect
// but the "real" solution is much more complicated.
if( $fulledge && !( $sa > 0 && $sa < M_PI && $ea < M_PI) ) {
 
if($sa < M_PI && $ea > M_PI)
$sa = M_PI;
if($sa < 2*M_PI && (($ea >= 2*M_PI) || ($ea > 0 && $ea < $sa ) ) )
$ea = 2*M_PI;
 
if( $sa >= M_PI && $ea <= 2*M_PI ) {
$p = array($xc + $w*cos($sa),$yc - $h*sin($sa),
$xc + $w*cos($sa),$z + $yc - $h*sin($sa));
for($a=$sa+$step; $a < $ea; $a += $step ) {
$p[] = $xc + $w*cos($a);
$p[] = $z + $yc - $h*sin($a);
}
$p[] = $xc + $w*cos($ea);
$p[] = $z + $yc - $h*sin($ea);
$p[] = $xc + $w*cos($ea);
$p[] = $yc - $h*sin($ea);
$img->SetColor($edgecolor);
$img->Polygon($p);
}
}
}
 
function Stroke($img,$aaoption=0) {
 
// If user hasn't set the colors use the theme array
if( $this->setslicecolors==null ) {
$colors = array_keys($img->rgb->rgb_table);
sort($colors);
$idx_a=$this->themearr[$this->theme];
$ca = array();
$n = count($idx_a);
for($i=0; $i < $n; ++$i)
$ca[$i] = $colors[$idx_a[$i]];
}
else {
$ca = $this->setslicecolors;
}
 
if( $this->posx <= 1 && $this->posx > 0 )
$xc = round($this->posx*$img->width);
else
$xc = $this->posx ;
if( $this->posy <= 1 && $this->posy > 0 )
$yc = round($this->posy*$img->height);
else
$yc = $this->posy ;
if( $this->radius <= 1 ) {
$width = floor($this->radius*min($img->width,$img->height));
// Make sure that the pie doesn't overflow the image border
// The 0.9 factor is simply an extra margin to leave some space
// between the pie an the border of the image.
$width = min($width,min($xc*0.9,($yc*90/$this->angle-$width/4)*0.9));
}
else {
$width = $this->radius * ($aaoption === 1 ? 2 : 1 ) ;
}
 
// Add a sanity check for width
if( $width < 1 ) {
JpGraphError::Raise("Width for 3D Pie is 0. Specify a size > 0");
exit();
}
 
// Establish a thickness. By default the thickness is a fifth of the
// pie slice width (=pie radius) but since the perspective depends
// on the inclination angle we use some heuristics to make the edge
// slightly thicker the less the angle.
// Has user specified an absolute thickness? In that case use
// that instead
 
if( $this->iThickness ) {
$thick = $this->iThickness;
$thick *= ($aaoption === 1 ? 2 : 1 );
}
else
$thick = $width/12;
$a = $this->angle;
if( $a <= 30 ) $thick *= 1.6;
elseif( $a <= 40 ) $thick *= 1.4;
elseif( $a <= 50 ) $thick *= 1.2;
elseif( $a <= 60 ) $thick *= 1.0;
elseif( $a <= 70 ) $thick *= 0.8;
elseif( $a <= 80 ) $thick *= 0.7;
else $thick *= 0.6;
 
$thick = floor($thick);
 
if( $this->explode_all )
for($i=0;$i<count($this->data);++$i)
$this->explode_radius[$i]=$this->explode_r;
 
$this->Pie3D($aaoption,$img,$this->data, $ca, $xc, $yc, $width, $this->angle,
$thick, 0.65, $this->startangle, $this->edgecolor, $this->edgeweight);
 
// Adjust title position
if( $aaoption != 1 ) {
$this->title->Pos($xc,$yc-$this->title->GetFontHeight($img)-$width/2-$this->title->margin, "center","bottom");
$this->title->Stroke($img);
}
}
 
//---------------
// PRIVATE METHODS
 
// Position the labels of each slice
function StrokeLabels($label,$img,$a,$xp,$yp,$z) {
$this->value->halign="left";
$this->value->valign="top";
$this->value->margin=0;
 
// Position the axis title.
// dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
// that intersects with the extension of the corresponding axis. The code looks a little
// bit messy but this is really the only way of having a reasonable position of the
// axis titles.
$img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
$h=$img->GetTextHeight($label);
// For numeric values the format of the display value
// must be taken into account
if( is_numeric($label) ) {
if( $label > 0 )
$w=$img->GetTextWidth(sprintf($this->value->format,$label));
else
$w=$img->GetTextWidth(sprintf($this->value->negormat,$label));
}
else
$w=$img->GetTextWidth($label);
while( $a > 2*M_PI ) $a -= 2*M_PI;
if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
$x = round($xp-$dx*$w);
$y = round($yp-$dy*$h);
 
/*
// Mark anchor point for debugging
$img->SetColor('red');
$img->Line($xp-10,$yp,$xp+10,$yp);
$img->Line($xp,$yp-10,$xp,$yp+10);
*/
 
$this->value->Stroke($img,$label,$x,$y);
}
} // Class
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/jpgraph_bar.php
New file
0,0 → 1,629
<?php
/*=======================================================================
// File: JPGRAPH_BAR.PHP
// Description: Bar plot extension for JpGraph
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_bar.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
//===================================================
// CLASS BarPlot
// Description: Main code to produce a bar plot
//===================================================
class BarPlot extends Plot {
var $width=0.4; // in percent of major ticks
var $abswidth=-1; // Width in absolute pixels
var $fill=false,$fill_color="lightblue"; // Default is to fill with light blue
var $ybase=0; // Bars start at 0
var $align="center";
var $grad=false,$grad_style=1;
var $grad_fromcolor=array(50,50,200),$grad_tocolor=array(255,255,255);
var $bar_shadow=false;
var $bar_shadow_color="black";
var $bar_shadow_hsize=3,$bar_shadow_vsize=3;
var $valuepos='top';
//---------------
// CONSTRUCTOR
function BarPlot(&$datay,$datax=false) {
$this->Plot($datay,$datax);
++$this->numpoints;
}
 
//---------------
// PUBLIC METHODS
// Set a drop shadow for the bar (or rather an "up-right" shadow)
function SetShadow($color="black",$hsize=3,$vsize=3) {
$this->bar_shadow=true;
$this->bar_shadow_color=$color;
$this->bar_shadow_vsize=$vsize;
$this->bar_shadow_hsize=$hsize;
// Adjust the value margin to compensate for shadow
$this->value->margin += $vsize;
}
// DEPRECATED use SetYBase instead
function SetYMin($aYStartValue) {
//die("JpGraph Error: Deprecated function SetYMin. Use SetYBase() instead.");
$this->ybase=$aYStartValue;
}
 
// Specify the base value for the bars
function SetYBase($aYStartValue) {
$this->ybase=$aYStartValue;
}
function Legend(&$graph) {
if( $this->grad && $this->legend!="" && !$this->fill ) {
$color=array($this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
$graph->legend->Add($this->legend,$color,"",0,
$this->legendcsimtarget,$this->legendcsimalt);
}
elseif( $this->fill_color && $this->legend!="" ) {
if( is_array($this->fill_color) )
$graph->legend->Add($this->legend,$this->fill_color[0],"",0,
$this->legendcsimtarget,$this->legendcsimalt);
else
$graph->legend->Add($this->legend,$this->fill_color,"",0,
$this->legendcsimtarget,$this->legendcsimalt);
}
}
 
// Gets called before any axis are stroked
function PreStrokeAdjust(&$graph) {
parent::PreStrokeAdjust($graph);
 
// If we are using a log Y-scale we want the base to be at the
// minimum Y-value unless the user have specifically set some other
// value than the default.
if( substr($graph->axtype,-3,3)=="log" && $this->ybase==0 )
$this->ybase = $graph->yaxis->scale->GetMinVal();
// For a "text" X-axis scale we will adjust the
// display of the bars a little bit.
if( substr($graph->axtype,0,3)=="tex" ) {
// Position the ticks between the bars
$graph->xaxis->scale->ticks->SetXLabelOffset(0.5,0);
 
// Center the bars
if( $this->align == "center" )
$graph->SetTextScaleOff(0.5-$this->width/2);
elseif( $this->align == "right" )
$graph->SetTextScaleOff(1-$this->width);
 
}
else {
// We only set an absolute width for linear and int scale
// for text scale the width will be set to a fraction of
// the majstep width.
if( $this->abswidth == -1 ) {
// Not set
// set width to a visuable sensible default
$this->abswidth = $graph->img->plotwidth/(2*count($this->coords[0]));
}
}
}
 
function Min() {
$m = parent::Min();
if( $m[1] >= $this->ybase )
$m[1] = $this->ybase;
return $m;
}
 
function Max() {
$m = parent::Max();
if( $m[1] <= $this->ybase )
$m[1] = $this->ybase;
return $m;
}
// Specify width as fractions of the major stepo size
function SetWidth($aFractionWidth) {
$this->width=$aFractionWidth;
}
// Specify width in absolute pixels. If specified this
// overrides SetWidth()
function SetAbsWidth($aWidth) {
$this->abswidth=$aWidth;
}
function SetAlign($aAlign) {
$this->align=$aAlign;
}
function SetNoFill() {
$this->grad = false;
$this->fill_color=false;
$this->fill=false;
}
function SetFillColor($aColor) {
$this->fill = true ;
$this->fill_color=$aColor;
}
function SetFillGradient($from_color,$to_color,$style) {
$this->grad=true;
$this->grad_fromcolor=$from_color;
$this->grad_tocolor=$to_color;
$this->grad_style=$style;
}
function SetValuePos($aPos) {
$this->valuepos = $aPos;
}
 
function Stroke(&$img,&$xscale,&$yscale) {
$numpoints = count($this->coords[0]);
if( isset($this->coords[1]) ) {
if( count($this->coords[1])!=$numpoints )
die("JpGraph Error: Number of X and Y points are not equal.<br>
Number of X-points:".count($this->coords[1])."<br>
Number of Y-points:$numpoints");
else
$exist_x = true;
}
else
$exist_x = false;
$numbars=count($this->coords[0]);
 
// Use GetMinVal() instead of scale[0] directly since in the case
// of log scale we get a correct value. Log scales will have negative
// values for values < 1 while still not representing negative numbers.
if( $yscale->GetMinVal() >= 0 )
$zp=$yscale->scale_abs[0];
else {
$zp=$yscale->Translate(0);
}
 
if( $this->abswidth > -1 ) {
$abswidth=$this->abswidth;
}
else
$abswidth=round($this->width*$xscale->scale_factor,0);
for($i=0; $i<$numbars; $i++) {
 
// If value is NULL, or 0 then don't draw a bar at all
if ($this->coords[0][$i] === null ||
$this->coords[0][$i] === '' ||
$this->coords[0][$i] === 0 ) continue;
 
if( $exist_x ) $x=$this->coords[1][$i];
else $x=$i;
$x=$xscale->Translate($x);
 
if( !$xscale->textscale ) {
if($this->align=="center")
$x -= $abswidth/2;
elseif($this->align=="right")
$x -= $abswidth;
}
 
$pts=array(
$x,$zp,
$x,$yscale->Translate($this->coords[0][$i]),
$x+$abswidth,$yscale->Translate($this->coords[0][$i]),
$x+$abswidth,$zp);
if( $this->grad ) {
$grad = new Gradient($img);
$grad->FilledRectangle($pts[2],$pts[3],
$pts[6],$pts[7],
$this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
}
elseif( !empty($this->fill_color) ) {
if(is_array($this->fill_color)) {
$img->PushColor($this->fill_color[$i % count($this->fill_color)]);
} else {
$img->PushColor($this->fill_color);
}
$img->FilledPolygon($pts);
$img->PopColor();
}
// Remember value of this bar
$val=$this->coords[0][$i];
if( $this->bar_shadow && $val !== 0 ) {
$ssh = $this->bar_shadow_hsize;
$ssv = $this->bar_shadow_vsize;
// Create points to create a "upper-right" shadow
if( $val > 0 ) {
$sp[0]=$pts[6]; $sp[1]=$pts[7];
$sp[2]=$pts[4]; $sp[3]=$pts[5];
$sp[4]=$pts[2]; $sp[5]=$pts[3];
$sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
$sp[8]=$pts[4]+$ssh; $sp[9]=$pts[5]-$ssv;
$sp[10]=$pts[6]+$ssh; $sp[11]=$pts[7]-$ssv;
}
elseif( $val < 0 ) {
$sp[0]=$pts[4]; $sp[1]=$pts[5];
$sp[2]=$pts[6]; $sp[3]=$pts[7];
$sp[4]=$pts[0]; $sp[5]=$pts[1];
$sp[6]=$pts[0]+$ssh; $sp[7]=$pts[1]-$ssv;
$sp[8]=$pts[6]+$ssh; $sp[9]=$pts[7]-$ssv;
$sp[10]=$pts[4]+$ssh; $sp[11]=$pts[5]-$ssv;
}
$img->PushColor($this->bar_shadow_color);
$img->FilledPolygon($sp);
$img->PopColor();
}
// Stroke the outline of the bar
if( is_array($this->color) )
$img->SetColor($this->color[$i % count($this->color)]);
else
$img->SetColor($this->color);
 
$pts[] = $pts[0];
$pts[] = $pts[1];
 
if( $this->weight > 0 ) {
$img->SetLineWeight($this->weight);
$img->Polygon($pts);
}
$x=$pts[2]+($pts[4]-$pts[2])/2;
if( $this->valuepos=='top' ) {
$y=$pts[3];
$this->value->Stroke($img,$val,$x,$y);
}
elseif( $this->valuepos=='center' ) {
$y = ($pts[3] + $pts[1])/2;
$this->value->SetAlign('center','center');
$this->value->SetMargin(0);
$this->value->Stroke($img,$val,$x,$y);
}
elseif( $this->valuepos=='bottom' ) {
$y=$pts[1];
$this->value->SetMargin(0);
$this->value->Stroke($img,$val,$x,$y);
}
else {
JpGraphError::Raise('Unknown position for values on bars :'.$this->valuepos);
die();
}
// Create the client side image map
$rpts = $img->ArrRotate($pts);
$csimcoord=round($rpts[0]).", ".round($rpts[1]);
for( $j=1; $j < 4; ++$j){
$csimcoord .= ", ".round($rpts[2*$j]).", ".round($rpts[2*$j+1]);
}
$this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
if( !empty($this->csimtargets[$i]) )
$this->csimareas .= " href=\"".$this->csimtargets[$i]."\"";
if( !empty($this->csimalts[$i]) ) {
$sval=sprintf($this->csimalts[$i],$this->coords[0][$i]);
$this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
}
$this->csimareas .= ">\n";
}
return true;
}
} // Class
 
//===================================================
// CLASS GroupBarPlot
// Description: Produce grouped bar plots
//===================================================
class GroupBarPlot extends BarPlot {
var $plots;
var $width=0.7;
var $nbrplots=0;
var $numpoints;
//---------------
// CONSTRUCTOR
function GroupBarPlot($plots) {
$this->plots = $plots;
$this->nbrplots = count($plots);
$this->numpoints = $plots[0]->numpoints;
}
 
//---------------
// PUBLIC METHODS
function Legend(&$graph) {
$n = count($this->plots);
for($i=0; $i<$n; ++$i)
$this->plots[$i]->DoLegend($graph);
}
function Min() {
list($xmin,$ymin) = $this->plots[0]->Min();
$n = count($this->plots);
for($i=0; $i<$n; ++$i) {
list($xm,$ym) = $this->plots[$i]->Min();
$xmin = max($xmin,$xm);
$ymin = min($ymin,$ym);
}
return array($xmin,$ymin);
}
function Max() {
list($xmax,$ymax) = $this->plots[0]->Max();
$n = count($this->plots);
for($i=0; $i<$n; ++$i) {
list($xm,$ym) = $this->plots[$i]->Max();
$xmax = max($xmax,$xm);
$ymax = max($ymax,$ym);
}
return array($xmax,$ymax);
}
function GetCSIMareas() {
$n = count($this->plots);
$csimareas='';
for($i=0; $i < $n; ++$i) {
$csimareas .= $this->plots[$i]->csimareas;
}
return $csimareas;
}
// Stroke all the bars next to each other
function Stroke(&$img,&$xscale,&$yscale) {
$tmp=$xscale->off;
$n = count($this->plots);
$subwidth = $this->width/$this->nbrplots ;
for( $i=0; $i < $n; ++$i ) {
$this->plots[$i]->ymin=$this->ybase;
$this->plots[$i]->SetWidth($subwidth);
// If the client have used SetTextTickInterval() then
// major_step will be > 1 and the positioning will fail.
// If we assume it is always one the positioning will work
// fine with a text scale but this will not work with
// arbitrary linear scale
$xscale->off = $tmp+$i*round(/*$xscale->ticks->major_step* */
$xscale->scale_factor*$subwidth);
$this->plots[$i]->Stroke($img,$xscale,$yscale);
}
$xscale->off=$tmp;
}
} // Class
 
//===================================================
// CLASS AccBarPlot
// Description: Produce accumulated bar plots
//===================================================
class AccBarPlot extends BarPlot {
var $plots=null,$nbrplots=0,$numpoints=0;
//---------------
// CONSTRUCTOR
function AccBarPlot($plots) {
$this->plots = $plots;
$this->nbrplots = count($plots);
$this->numpoints = $plots[0]->numpoints;
$this->value = new DisplayValue();
}
 
//---------------
// PUBLIC METHODS
function Legend(&$graph) {
$n = count($this->plots);
for( $i=$n-1; $i>=0; --$i )
$this->plots[$i]->DoLegend($graph);
}
 
function Max() {
list($xmax) = $this->plots[0]->Max();
$nmax=0;
for($i=0; $i<count($this->plots); ++$i) {
$n = count($this->plots[$i]->coords[0]);
$nmax = max($nmax,$n);
list($x) = $this->plots[$i]->Max();
$xmax = max($xmax,$x);
}
for( $i = 0; $i < $nmax; $i++ ) {
// Get y-value for bar $i by adding the
// individual bars from all the plots added.
// It would be wrong to just add the
// individual plots max y-value since that
// would in most cases give to large y-value.
$y=$this->plots[0]->coords[0][$i];
for( $j = 1; $j < $this->nbrplots; $j++ ) {
$y += $this->plots[ $j ]->coords[0][$i];
}
$ymax[$i] = $y;
}
$ymax = max($ymax);
 
// Bar always start at baseline
if( $ymax <= $this->ybase )
$ymax = $this->ybase;
return array($xmax,$ymax);
}
 
function Min() {
$nmax=0;
list($xmin,$ysetmin) = $this->plots[0]->Min();
for($i=0; $i<count($this->plots); ++$i) {
$n = count($this->plots[$i]->coords[0]);
$nmax = max($nmax,$n);
list($x,$y) = $this->plots[$i]->Min();
$xmin = Min($xmin,$x);
$ysetmin = Min($y,$ysetmin);
}
for( $i = 0; $i < $nmax; $i++ ) {
// Get y-value for bar $i by adding the
// individual bars from all the plots added.
// It would be wrong to just add the
// individual plots max y-value since that
// would in most cases give to large y-value.
$y=$this->plots[0]->coords[0][$i];
for( $j = 1; $j < $this->nbrplots; $j++ ) {
$y += $this->plots[ $j ]->coords[0][$i];
}
$ymin[$i] = $y;
}
$ymin = Min($ysetmin,Min($ymin));
// Bar always start at baseline
if( $ymin >= $this->ybase )
$ymin = $this->ybase;
return array($xmin,$ymin);
}
 
// Stroke acc bar plot
function Stroke(&$img,&$xscale,&$yscale) {
$img->SetLineWeight($this->weight);
for($i=0; $i<$this->numpoints-1; $i++) {
 
 
$accy = 0;
$accy_neg = 0;
for($j=0; $j < $this->nbrplots; ++$j ) {
$img->SetColor($this->plots[$j]->color);
 
if ( $this->plots[$j]->coords[0][$i] >= 0) {
$yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
$accyt=$yscale->Translate($accy);
$accy+=$this->plots[$j]->coords[0][$i];
}
if ( $this->plots[$j]->coords[0][$i] < 0 || $accy_neg < 0 ) {
$yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
$accyt=$yscale->Translate($accy_neg);
$accy_neg+=$this->plots[$j]->coords[0][$i];
}
$xt=$xscale->Translate($i);
 
if( $this->abswidth > -1 )
$abswidth=$this->abswidth;
else
$abswidth=round($this->width*$xscale->scale_factor,0);
$pts=array($xt,$accyt,$xt,$yt,$xt+$abswidth,$yt,$xt+$abswidth,$accyt);
 
if( $this->bar_shadow ) {
$ssh = $this->bar_shadow_hsize;
$ssv = $this->bar_shadow_vsize;
// We must also differ if we are a positive or negative bar.
if( $j === 0 ) {
// This gets extra complicated since we have to
// see all plots to see if we are negative. It could
// for example be that all plots are 0 until the very
// last one. We therefore need to save the initial setup
// for both the negative and positive case
 
// In case the final bar is positive
$sp[0]=$pts[6]+1; $sp[1]=$pts[7];
$sp[2]=$pts[6]+$ssh; $sp[3]=$pts[7]-$ssv;
 
// In case the final bar is negative
$nsp[0]=$pts[0]; $nsp[1]=$pts[1];
$nsp[2]=$pts[0]+$ssh; $nsp[3]=$pts[1]-$ssv;
$nsp[4]=$pts[6]+$ssh; $nsp[5]=$pts[7]-$ssv;
$nsp[10]=$pts[6]+1; $nsp[11]=$pts[7];
}
 
if( $j === $this->nbrplots-1 ) {
// If this is the last plot of the bar and
// the total value is larger than 0 then we
// add the shadow.
if( $accy > 0 ) {
$sp[4]=$pts[4]+$ssh; $sp[5]=$pts[5]-$ssv;
$sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
$sp[8]=$pts[2]; $sp[9]=$pts[3]-1;
$sp[10]=$pts[4]+1; $sp[11]=$pts[5];
$img->PushColor($this->bar_shadow_color);
$img->FilledPolygon($sp,4);
$img->PopColor();
}
elseif( $accy_neg < 0 ) {
$nsp[6]=$pts[4]+$ssh; $nsp[7]=$pts[5]-$ssv;
$nsp[8]=$pts[4]+1; $nsp[9]=$pts[5];
$img->PushColor($this->bar_shadow_color);
$img->FilledPolygon($nsp,4);
$img->PopColor();
}
}
}
 
// If value is NULL or 0, then don't draw a bar at all
if ($this->plots[$j]->coords[0][$i] == 0 ) continue;
 
if( $this->plots[$j]->grad ) {
$grad = new Gradient($img);
$grad->FilledRectangle(
$pts[2],$pts[3],
$pts[6],$pts[7],
$this->plots[$j]->grad_fromcolor,
$this->plots[$j]->grad_tocolor,
$this->plots[$j]->grad_style);
} elseif ($this->plots[$j]->fill_color ) {
$img->SetColor($this->plots[$j]->fill_color);
$img->FilledPolygon($pts);
$img->SetColor($this->plots[$j]->color);
}
 
 
// CSIM array
 
if( $i < count($this->plots[$j]->csimtargets) ) {
// Create the client side image map
$rpts = $img->ArrRotate($pts);
$csimcoord=round($rpts[0]).", ".round($rpts[1]);
for( $k=1; $k < 4; ++$k){
$csimcoord .= ", ".round($rpts[2*$k]).", ".round($rpts[2*$k+1]);
}
$this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
$this->csimareas.= " href=\"".$this->plots[$j]->csimtargets[$i]."\"";
if( !empty($this->plots[$j]->csimalts[$i]) ) {
$sval=sprintf($this->plots[$j]->csimalts[$i],$this->plots[$j]->coords[0][$i]);
$this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
}
$this->csimareas .= ">\n";
}
 
$pts[] = $pts[0];
$pts[] = $pts[1];
$img->Polygon($pts);
}
// Draw labels for each acc.bar
$x=$pts[2]+($pts[4]-$pts[2])/2;
$y=$yscale->Translate($accy);
if($this->bar_shadow) $x += $ssh;
$this->value->Stroke($img,$accy,$x,$y);
 
$accy = 0;
$accy_neg = 0;
for($j=0; $j<$this->nbrplots; ++$j ) {
if ($this->plots[$j]->coords[0][$i] > 0) {
$yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
$accyt=$yscale->Translate($accy);
$y = $accyt-($accyt-$yt)/2;
$accy+=$this->plots[$j]->coords[0][$i];
} else {
$yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
$accyt=$yscale->Translate($accy_neg);
$y=0;
$accy_neg+=$this->plots[$j]->coords[0][$i];
}
$this->plots[$j]->value->SetAlign("center","center");
$this->plots[$j]->value->SetMargin(0);
$this->plots[$j]->value->Stroke($img,$this->plots[$j]->coords[0][$i],$x,$y);
}
 
}
return true;
}
} // Class
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/jpgraph_scatter.php
New file
0,0 → 1,223
<?php
/*=======================================================================
// File: JPGRAPH_SCATTER.PHP
// Description: Scatter (and impuls) plot extension for JpGraph
// Created: 2001-02-11
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_scatter.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
 
//===================================================
// CLASS FieldArrow
// Description: Draw an arrow at (x,y) with angle a
//===================================================
class FieldArrow {
var $iSize=10; // Length in pixels for arrow
var $iArrowSize = 2;
var $iColor='black';
var $isizespec = array(
array(2,1),array(3,2),array(4,3),array(6,4),array(7,4),array(8,5),array(10,6),array(12,7),array(16,8),array(20,10));
function FieldArrow() {
}
 
function SetSize($aSize,$aArrowSize=2) {
$this->iSize = $aSize;
$this->iArrowSize = $aArrowSize;
}
 
function SetColor($aColor) {
$this->iColor = $aColor;
}
 
function Stroke($aImg,$x,$y,$a) {
$old_origin = $aImg->SetCenter($x,$y);
$old_a = $aImg->SetAngle(-$a);
 
$dx = round($this->iSize/2);
$c = array($x-$dx,$y,$x+$dx,$y);
$x += $dx;
 
list($dx,$dy) = $this->isizespec[$this->iArrowSize];
$ca = array($x,$y,$x-$dx,$y-$dy,$x-$dx,$y+$dy,$x,$y);
 
$aImg->SetColor($this->iColor);
$aImg->Polygon($c);
$aImg->FilledPolygon($ca);
 
$aImg->SetCenter($old_origin[0],$old_origin[1]);
$aImg->SetAngle($old_a);
}
}
 
//===================================================
// CLASS FieldPlot
// Description: Render a field plot
//===================================================
class FieldPlot extends Plot {
var $iAngles;
var $iCallback='';
function FieldPlot($datay,$datax,$angles) {
if( (count($datax) != count($datay)) )
JpGraphError::Raise("Fieldplots must have equal number of X and Y points.");
if( (count($datax) != count($angles)) )
JpGraphError::Raise("Fieldplots must have an angle specified for each X and Y points.");
$this->iAngles = $angles;
 
$this->Plot($datay,$datax);
$this->value->SetAlign('center','center');
$this->value->SetMargin(15);
 
$this->arrow = new FieldArrow();
}
 
function SetCallback($aFunc) {
$this->iCallback = $aFunc;
}
 
function Stroke(&$img,&$xscale,&$yscale) {
 
// Remeber base color and size
$bc = $this->arrow->iColor;
$bs = $this->arrow->iSize;
$bas = $this->arrow->iArrowSize;
 
for( $i=0; $i<$this->numpoints; ++$i ) {
// Skip null values
if( $this->coords[0][$i]==="" )
continue;
 
$f = $this->iCallback;
if( $f != "" ) {
list($cc,$cs,$cas) = $f($this->coords[1][$i],$this->coords[0][$i],
$this->iAngles[$i]);
// Fall back on global data if the callback isn't set
if( $cc == "" ) $cc = $bc;
if( $cs == "" ) $cs = $bs;
if( $cas == "" ) $cas = $bas;
//echo "f=$f, cc=$cc, cs=$cs, cas=$cas<br>";
$this->arrow->SetColor($cc);
$this->arrow->SetSize($cs,$cas);
}
 
$xt = $xscale->Translate($this->coords[1][$i]);
$yt = $yscale->Translate($this->coords[0][$i]);
 
$this->arrow->Stroke($img,$xt,$yt,$this->iAngles[$i]);
$this->value->Stroke($img,$this->coords[0][$i],$xt,$yt);
}
}
// Framework function
function Legend(&$aGraph) {
if( $this->legend != "" ) {
$aGraph->legend->Add($this->legend,$this->mark->fill_color,$this->mark,0,
$this->legendcsimtarget,$this->legendcsimalt);
}
}
}
 
//===================================================
// CLASS ScatterPlot
// Description: Render X and Y plots
//===================================================
class ScatterPlot extends Plot {
var $impuls = false;
var $linkpoints = false, $linkpointweight=1, $linkpointcolor="black";
//---------------
// CONSTRUCTOR
function ScatterPlot($datay,$datax=false) {
if( (count($datax) != count($datay)) && is_array($datax))
JpGraphError::Raise("Scatterplot must have equal number of X and Y points.");
$this->Plot($datay,$datax);
$this->mark = new PlotMark();
$this->mark->SetType(MARK_SQUARE);
$this->mark->SetColor($this->color);
$this->value->SetAlign('center','center');
$this->value->SetMargin(0);
}
 
//---------------
// PUBLIC METHODS
function SetImpuls($f=true) {
$this->impuls = $f;
}
 
// Combine the scatter plot points with a line
function SetLinkPoints($aFlag=true,$aColor="black",$aWeight=1) {
$this->linkpoints=$aFlag;
$this->linkpointcolor=$aColor;
$this->linkpointweight=$aWeight;
}
 
function Stroke(&$img,&$xscale,&$yscale) {
 
$ymin=$yscale->scale_abs[0];
if( $yscale->scale[0] < 0 )
$yzero=$yscale->Translate(0);
else
$yzero=$yscale->scale_abs[0];
$this->csimareas = '';
for( $i=0; $i<$this->numpoints; ++$i ) {
 
// Skip null values
if( $this->coords[0][$i]==="" )
continue;
 
if( isset($this->coords[1]) )
$xt = $xscale->Translate($this->coords[1][$i]);
else
$xt = $xscale->Translate($i);
$yt = $yscale->Translate($this->coords[0][$i]);
 
 
if( $this->linkpoints && isset($yt_old) ) {
$img->SetColor($this->linkpointcolor);
$img->SetLineWeight($this->linkpointweight);
$img->Line($xt_old,$yt_old,$xt,$yt);
}
 
if( $this->impuls ) {
$img->SetColor($this->color);
$img->SetLineWeight($this->weight);
$img->Line($xt,$yzero,$xt,$yt);
}
if( !empty($this->csimtargets[$i]) ) {
$this->mark->SetCSIMTarget($this->csimtargets[$i]);
$this->mark->SetCSIMAlt($this->csimalts[$i]);
}
if( isset($this->coords[1]) ) {
$this->mark->SetCSIMAltVal($this->coords[0][$i],$this->coords[1][$i]);
}
else {
$this->mark->SetCSIMAltVal($this->coords[0][$i],$i);
}
 
$this->mark->Stroke($img,$xt,$yt);
$this->csimareas .= $this->mark->GetCSIMAreas();
$this->value->Stroke($img,$this->coords[0][$i],$xt,$yt);
 
$xt_old = $xt;
$yt_old = $yt;
}
}
// Framework function
function Legend(&$aGraph) {
if( $this->legend != "" ) {
$aGraph->legend->Add($this->legend,$this->mark->fill_color,$this->mark,0,
$this->legendcsimtarget,$this->legendcsimalt);
}
}
} // Class
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/imgdata_pushpins.inc
New file
0,0 → 1,519
<?php
//=======================================================================
// File: IMGDATA_PUSHPINS.INC
// Description: Base64 encoded images for pushpins
// Created: 2003-03-20
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: imgdata_pushpins.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
class ImgData_PushPins extends ImgData {
var $name = 'Push pins';
var $an = array(MARK_IMG_PUSHPIN => 'imgdata_small',
MARK_IMG_SPUSHPIN => 'imgdata_small',
MARK_IMG_LPUSHPIN => 'imgdata_large');
 
var $colors = array('blue','green','orange','pink','red');
var $index = array('red' => 0, 'orange' => 1, 'pink' => 2, 'blue' => 3, 'green' => 4 ) ;
var $maxidx = 4 ;
var $imgdata_large, $imgdata_small ;
 
function ImgData_PushPins() {
 
// The anchor should be where the needle "hits" the paper
// (bottom left corner)
$this->anchor_x = 0;
$this->anchor_y = 1;
 
//==========================================================
// File: ppl_red.png
//==========================================================
$this->imgdata_large[0][0]= 2490 ;
$this->imgdata_large[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMKBh4Ryh89CgAACUdJREFUeJy9mNtTFFcexz+/7p'.
'4Lw1wZJKDGCAwmDAqUySamcCq1ed6k9mn3UfMP7F+1T3nYqn2J'.
'lZdoDEjpbq0KG8EBFBFBEJye6Zmenkv32Ydu5GYiUMmeqq6uqT'.
'6Xz3zP73aOcIKmAQkIFyD3N/jrBPwlKjLQEglVlJKyUjR3u7cc'.
'WLoP3/4dvv03LNrQ8I6x1rFbDML9kOmHvh7IRHU9JKmUSG8vpF'.
'IoXX/TV0AiEM5A5jT0noFMFMJHXUt/d5f9TUAbhtQ3cPFruDog'.
'8klHMnmO0dGYe/myOJGINEwTz3F2higFXgy8PpAkOC+h8hoaCt'.
'4ppHFcQAWSgOQlyI/p+lUjmRxWAwNJd3xca/f34yoFi4tgmjtD'.
'NIFkJ4xcgBCgVqEBFJ9DqcZea/gNAAVEg7AOGYnHe9XoaJd3+X'.
'LISSSwnz6lsbKCZ9sHh4UVdBkwdA6cPwNnIfJPmC3Ctgft3wwQ'.
'QPkvTZJJnbExzfvsM2nMzVG7e5fG48d4lnXwTwEYCjJxuHQBog'.
'BHUfKkgAIIhiGk06hTp/Dm5qS1uYlXLvtWd4gPgIiCrAEcVckT'.
'Ab5p7TaYJrK1hQaEenrwSiVfQdc91P0kSp7Ii89D5ksY/kAkLy'.
'IZXFdXkQjS1YUSEbdcRu168V6+HTUNIKJDRwdE+sBIQmP9Ld59'.
'bEBA3of4F/D+uXb7rGaaCSmXI3pPj64PDaHCYfEqFVSjgWo2D2'.
'73XlJNQTgCyQykIuBWoNKEeh1aLXBPBCggGdBOgxZVSjoajVhH'.
'o5HWlIpq4bCQSgm9vXhK4ZZKh5SUYygp4J1EQVUD9xlU18BJQD'.
'bUbJ5T5XJStyxN9fSI099P3baxV1dRloW2h2ivx/yakg2ot6F1'.
'EkCa4G1D+zVEq5ArKTWM42Q6HUczQV7U66w9e0ZpdRXlOIQ5vF'.
'VHUXILKify4jiEzkOqC3peQMoBQymFlMt4Dx6wUSxSsm2UZXEK'.
'P30QvOUt8/2Sd78CdWwFDTA+gsw3cOlPcPUD+CQB52oQ21RKXM'.
'eRhGXhOg7VoKrx8KuS4ygZhVg3ZI8FGIfwR9BVgAtfwxdXdP3L'.
'86nUR91dXelNXTeWWy10paQHX602YAP1ADASAL7LJvFtMpOCc0'.
'cG3FHuGlz6Gr4YEpnoTCbzsdHRbOzy5RCRiLRMk5rjyOtAimwA'.
'U4U3SurBN/0wnAASBCVDIKpB4kiAB5Ub0/UvO9LpPAMDGfn005'.
'AxPCzxep3Q6iqPLUseBoufCZRsAE6g5g5kKIDfKUj3wnpAG8QB'.
'/Z1OIqANQuI65AtwNScyYXR2XlAXL2YZHzcklRKWl5GVFXFtGx'.
'MoAiV/EQaAGH6BUQNWgQpwFngv+Ca8KUAQEBcwgTJHyMV7679R'.
'XS8YqdSI6u/PMD5ukMtJY3GR2uQkr5aXeWVZOEALmA8WsIAxfL'.
'd0goVLAdCOd+/YpgqeVtBv4yiA++q/RKKXixe7GB8PSyoljcVF'.
'yg8fyubyMpulEk2lyAIfAAvAC+B+oOQFoAt/+0rAejB/EzjNri'.
'vvqNnCd64jxcE39V8spnP+vMbAgDSePKE2NcXm06dslMuUlcID'.
'TuFvqwXMBU8N39bGgRR+ki0Dz4L5DSAe9NGD7zq+6kcN1L6H2b'.
'ao5WWaQHllRTafPmWrVMJUimoAQrBYJFjQwre7B6A8YAi8LCgD'.
'5DVo6/hbb/iHK1KggvFeD3hHziQKEMuiNTNDbXGRTdtmw7Iwla'.
'KGH0oqwbscLOoG46rAY6AOzRhY74PT6QuUKEN4PegXxd/yEDTT'.
'YMWOk+oEaLkuFdNk0zTZwjfkavDUArXWgGXgFb4dEShXhfYqlI'.
'ow3w9rg3B6ED60IOOA5oEYQBrcpG+mj9bg0VG8GMJhVDZLyzAo'.
'VSq8rFYxXXefcjVgG9+uisDrXUCApoKSBcUHMBmHhfcgNwhtD3'.
'q9IG6Lr15b4OUTmPwBJt8JqGuapp05o0mhoHnptLQfPsR+8IBK'.
'uYyNH3yr+B77LHheA3tK1Ta+IrMeTL2C6Xl48TOsNWDDgAz7s5'.
'/r+krP/eddCsbj8fDQ4GBm9MqVvvRXX2VULBayRGRzaYn1SoWa'.
'UjgB4PIB5QK4ZgBXBKaAHxQsrED1H7CRgCUPwgHZDqACmhWwXv'.
'2aDRqGYeRyufS169cvThQKV88PDuYbW1vJ5VRK+5euqxWlPMdX'.
'SRqgreHbZGN3ijfKBXBTAeh2Fdwi2MofshP/dvKwCmKhp4m83Y'.
'vj8Xg4l8tlCoXC0MTExMTFkZE/1m37wvLGRvKRacoD1209E7Fc'.
'pZwYREOQqEJ4z3HskHLsz4AoXykPIBSN0t3dTTQafROoHdumXC'.
'4fjoMiog0ODiauX7+eLxQKV3O53ETdti88nJnJ3rl505ifmWm3'.
'arWSodR8GNbycDoNHy5C5jFold1k8d+DyvELNwg93d18/vnn9P'.
'X1oes6nufx/Plz7t+/fxhQKSWJRCI5NjaWHxkZKdj1+sjSwkJm'.
'+uZN/dZ337VqCwullGUVdZjsgIUC5LqhrUPvCugWuApeApPAzY'.
'PKHWyaphGNRunt7WVwcBARwfM8Ojo6sCzrMKBhGLphGFEF2Wq1'.
'2jc7M5OZ/vHH0MPbt93awkJJmeZsC6ZaMK3DCwvWdNioQUb5B6'.
'AdBR+9SzkAz/NwHIeXL18iIui6TjgcJplMMjY2th8wHo+Hh4aG'.
'MsPDw6fddru7+Phxx51bt/RbN260qwsLpZhlFZsw9QJ+2Pbrga'.
'oJG2FY2oKwuTtVEz9uV34NbqdtbW0xPT1NNBoF4MyZM1y5coWu'.
'rq5dQBHRcrlc4tq1a/l8Pj9RMs38ndu3Ez//9JNXLRZNyuXZJk'.
'xVYKoExQpsK/+IaAuYb7no8zjC/R+A4zisrq7u+53NZjl16tQ+'.
'QIlEIslsNpuPRCJXZ2dnh2/duNFRW1oy07a96MKd575yxRqU1B'.
'5vPMpF5HHa1tYW9+7do7Ozc/eQpZTSQ6FQt1Lq8pMnT/5w7969'.
'nuLcXE1rNufO9fRMhlKpOyvt9qPtVmvb25fFfvvWbrepVCqHwo'.
'xaX19vff/996ZhGC8qlkW9Wt1Onz073fXxxz+6MB+9e9dUjuO+'.
'7ebq9wLdB9hoNCrr6+s/4wf3FCJW3fPmTZhXsNWCprjuW66Dfr'.
'928KAfBhJAEgiJSLuzs7OSTqctoFkqlZRt26j/I+L/AGjPTN4d'.
'Nqn4AAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: ppl_orange.png
//==========================================================
$this->imgdata_large[1][0]= 2753 ;
$this->imgdata_large[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMLFQ0VCkHCzQAACk5JREFUeJytmGtzG0d2hp8zNw'.
'AEcRdJ6EJK9FL0CqZUm9jWbkwq3vhDstl8dmLvz8rP2H8Q75ZT'.
'pkRfpLgqsS6WIFEKGYkiSBCDO+banQ8DUpRWEkklXQUUqlCDfv'.
'rp857pgfAOQ4AMOJdg4R/hX96Hf06bvDc5iT07i8yeg8ksiIAI'.
'4TBi/ds9/vivD/njapNHvRBfHXMu410AM+BUoVSF05NQsi1sO4'.
'8402AXwLQTuP31OAZO2aG0MEn14iSlnI1z3LnMk8IZYJyBwjIs'.
'/TWsVIWPJkvMFS4zMfMhUp5BsoCpAAEBLYKaMFGn00jBxnvu02'.
'35+JHmSJEnBpQEcPo38MmCxd/nS9Ry71Ga/g1W9a8gn0GsHkgA'.
'6DGjxkqb5CoO+YxF3A3p+jGjQUzoK+L/V0ADzFMwtSR8eLbAr8'.
'uXOTf9NzhTc0geSLUQcYHgYEH786RMg0zWJHV2Aitv4x/HpHVS'.
'QA2YBqTTGIUq5qkPMWaWkVwPnPtAA/BevmZcjxaaUtHh8pJJGu'.
'DpCB9FvT7A7YT7S3p5vFMNzmWo/O0MSx/Ms3TqI8r59zFTfUQe'.
'I7SBODE3tnfoIxYnNHligwik0zAzDdVpyKbA8sff5YAeMEwgkV'.
'cufQeTJzZoCsaFLKXPTnNpoUTNsSgJmNoGsuNQjIDwYD2HlnZy'.
'k++yxTKXZfKTU8zOpjhneeQYkorSmGERtIlICBKRbLX+y98YN3'.
'ADcNIm+bJD4U3pPnmbEaRgYVRTGBkDSSsmxKfY7ZLuDJA4hdjl'.
'JEgyBB2SJOvQ9RzTpNKoEwNq0CNFvOXR3/HxMgYVPObaz8kPmh'.
'hkEWMatAfRONGGvLizyOE9P8KkpwhPDAgQKJQbELUD0oOIhbbH'.
'JeVTmowxjAgZutB5AoOngA+2DdYrcTyOyYZP9+QpBvI29vwEhb'.
'It042BVQgDy9KTMfkwQG1A9ACCLlgBBGUwxxoc52WDh2ATyEPp'.
'1hoaPvrEBh0Dq5an9OUsl/9hylk5b5c+mowLc4E2Jtw4Eoljyf'.
'ogA/AGEAagNRjGyUxOmEycyVA5EWDBxrmUp3ytLIv/NJP69Goh'.
'+9mFydIvS5PZYkvH1oY/RFtKymlwBFQAgQd+kAA6qSQ8pvn2mp'.
'SkJkuVFHPHBnQMrEt5Sl+e4/Lvp51PF1PF5Xy6WMvOWZXMom8z'.
'OZTQ8+j5sbQiMEwopsCIwRtBGIJSCdzbTGo9NimkDcgdC7Bg49'.
'TG5n4/nfr0Si77WdYp1YzyZEkWPdteaEnB7pPqBTxuIf/VgciE'.
'SgasCPwh+GNIkaNNag1RiPge5pEhMQVjfoLcF+eoXSvbKxedwn'.
'LKzC3KWbOi5/sW5a44/SHFUSgVA7SCzRG0AvA9mPOgFIETgu4n'.
'Ww0wNQWFAqRSL6D2ZQYBdDrQ7R7jXiwgRcvIL02makuTmWtpM/'.
'+BlLMl5vuWzLVEuwH6oYnR1KS8kJINGXMM2YdfRlALoQoQQKeb'.
'bDVwoMdxQMaLCwLo96HZTF5HbrEhmOftianfZisfzueKv7ZmrX'.
'MsjhxKXZGBjzyeEHmSE3oWiggtyVGmE8DTIXTC5NxgAxOAGUM8'.
'fun9mnSSLQ/CxNzOTgJ3LIMgoGwkKBiiMyaVviHVkdCO4FEKNv'.
'LQzWBYHfITPa4UBVM0LR/WB7ARJsdDDTjA6deYFIFUOimJ3d0E'.
'sNdLavYYgBpthqKcjiiJRO8K6CK0CsJTjfQAGaJtD9vQFAxNNQ'.
'1FB0yBAfA8gdMAIagLoCVAen0M00zMOTYShNDtoHs9CAIUoI4E'.
'1IBihCdNhsMhsj6NuV7BCC2IBpBqQaaFOENCCeiEsO1BO4RQgy'.
'I5Hm4k4oIU9MrgZSAdBeTabZz+ODxKQRRBFBJo6IUc51anYRQo'.
'dto+24FNxYCiaWKkQsj00KkO4gxRRkAngJ868M0u3OkkM+hxQA'.
'cQ7YD7GO5XYSsPZybh/TCkFIYY+kWniTW4Q7jXgHvHMhiRpmuW'.
'ca08GZkkZ/nY6TZMNhCnf2CuPoDVJvxpB+q9BHA8Ag1uH+oP4c'.
'YEPCzDwmzSLquShHW/E0YRbG/BjZtw40hAy7aNzJlzRn75E6N0'.
'qiwTzafI7kOU3gWrhzZC2iHcbsPqLlxvJnCt4KC1RYAL3I5hzY'.
'Xv/huePYCtITQMKEnyB4KQvMURuJvw889HGSwUCs7CwkLpo6tX'.
'Ty/+7nel6VLGDn/8N9m+eZuo1UP8iNhLau6b3RfmOsHBGTUYw9'.
'WBNeDrGB4+h/4qNLKwTnLbHj9CJw/6GoIh9Jpvq0HHcayFhYXi'.
'l3/4w9LK8vLKexfma3G/mb/3n1njTivS7tNQaaU1grQDjJ868D'.
'Axx6vmxnBrY9C9IcSbSXbavNjb/S3eN6/0m1JcKBScixcvllZW'.
'Vi6uLC8v12q1v/M8b/HxVjP//YYr32yE4dYWvShO0ogi14xwxq'.
'F4rbnxZ3cMjtpvEEeMvwA0TdOYn5/PffHFF7Vr166tvPeLXyx7'.
'nrd4+/btyg/frFo//Xgncnd67qCn78earQqcmYD3fSi1wPCTSV'.
'3gzqvm9uFOMl5nUAqFQn5paal26dKla57vf7D+6FHph9VV88af'.
'vgq79bo70e3VT2l9A3hYg4UiRALVHTCHSZvYBm4A//6quf8zoG'.
'3bpuM4acMwKr1+//SDe/dK31+/bv90/Xrcq9fduNW6rbVeC+E7'.
'gWdD2DKg4UEpBmPcm10RuScida31ntb62HAigoigDw6Gh0axWH'.
'QWFhZKi4uLZ+I4PrVer2e+u37dXPvqq6hbr7tOp1NXWq89h6/b'.
'8FBB34WGBesdcPrj38lkMkGlUuml0+mu53nR3t4eo9HoSLhMJk'.
'OlUiGdTuN5Hq7rvgA0TdO4cOFC7vPPP6/VarXldqdTu7m2lrv7'.
'7beq++BBO263b/tKrfWSXlbvwJ6CuAtDgTYiaBFMw6BSqfDxxx'.
'+rarWqGo0GN2/eZGtrC6XenAkRoVKpcPXqVWZmZmg0Gty6desF'.
'oIhIOp3Ol8vlmmVZK3fv3Lm09uc/Zwbr653ccPgoNIzvnmn99Z'.
'7W9QG46lAaM5mM2l95GIYUi0VOnz7N7OwsWmsymQzyuse5Q8Mw'.
'DNLpNDMzM5w/f/7A6AGgUkoajYa9urpayOXzUz/fvZutr68Pim'.
'F4/2y1+n2o9Q/ru7uPesPhXnyo4A+vfHp6mmazybNnz9jZ2UFr'.
'TbPZJAhe+8/aS0Mphed5NBoNABqNBqPR6MWBVWstvu/nnj9/Pv'.
'vo0aPq5uZmPBgM/qcwPf39xV/9ajU1M3Nvq9PZaw8GoT50PjdN'.
'k6mpKa5cucL58+eJ45j19XWePHnCzs4OnudhmiaWZRGGIVH05r'.
'yEYYjrumxubrKxsfFyDQJ6NBp1Pc+7C4jWumBaVm+kVL2l1H2l'.
'1G6otS+H6V6z8u3tbVzXpdFooJRicXGRqakptre3uXXr1ltrcT'.
'Qa8ezZszemWAE9rfUdYBOwtVLRbrPZ+48ff+wDvuu6Sr3MB4Dr'.
'uty6desgfa1WC3iRyrNnz4pSSmezWUzTfGtYtNYcdvC/9sMlgP'.
'n5N4cAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: ppl_pink.png
//==========================================================
$this->imgdata_large[2][0]= 2779 ;
$this->imgdata_large[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMLFQolY9lkpgAACmhJREFUeJy9mOtzFNl5h5+3b9'.
'Mz0kzPBWmEVtIiWYhIiC0HCDhB8lb8ISk7nzdZ5+/zJ/8BTmpT'.
'660CZLwG1pVFgBkgGIHECEaa+/T9nHzQCCQuRpCNz6mp6g893U'.
'8/c37ve3qEjxiC4OA4n/Lp/EUu/tsMM/+aEWduVBx7WhdkShcY'.
'xUH2zo0Dwod/5N6vf8V//PoGdx8M8EOFPtK9jI8BdHCcMuVSmf'.
'LxHLmSZdm2U8xIbmKETDGDZZnIy4dBbCynyGhphurEDBOlHFnn'.
'qPcyPxTOwDCOccw7w5nlBRZWylI+ny/mZ6rL1dzUZ5/IWGZU3D'.
'ZIOMQDDaJcHDVGWUbJBi9odVr0QoVSPzigIEaZ8vgSS/8wZU3/'.
'k1fylipz5dLM2WlrZqHKaGCKbEbontq3KAKWQyZfZKTgYqc9Bp'.
'2I2PcJ4ogk/UEBQcwipbFZmT13vDBx8fhnE1Ofnp9yJopFyT3X'.
'yANfks0QHSQMDaL37pOxMLIu2UyVkjVKLjyKSeuD8dAYCFkso1'.
'gYMaeWJ40T56cl8yAi/O4FSa2P6kYczIDsgVpAqcDImZPMuAB1'.
'dkLQtcc8a/bwox8IUHAxZVxGZMouSLVYwKuMkD5IxN+JSdsRJB'.
'pexuTVgYYM6EoGmxkmg3/hEhNUMr/hd7dqbOzExMn/GRDAxWZc'.
'j3I8HiXfMjF2FQowKw7pjoN6E/Llw/GBJj8qxVOMlX4ipxc/lY'.
'kl2zBLkmrTcEzMkoNoRLVidLi/9g+Z3I+1xRHX5EcAihxnbPRv'.
'OTU9kZSmpKPy9FTGrLimPZ1H+UiyGaF67w6n7E1DwMngFDxGvc'.
'w70v0xZUby5IxjlIyMssUJrJwVWkXBdbXvSvwEibcSdKCAFI16'.
'4/sc0SRo9cGAGq1DwvQFzV6DVuBiV4zYnlEts6A2TSPcSiXoxo'.
'QqJCEEFMbQ2b69o5qMiOOPqIMQkagu/aSL7waE8101WFShLjk9'.
'yxgEvjRUiyYd+gwAjY2J9VpXfZ/JEXLhDp3OR6U4T97+hEnPwx'.
'tv4HsRjy2tTQSFzQgDUnwSLBQRI+x1ZgcH87Vcv4SF19Kt0ezS'.
'1h9s0Ma25pgr/YJfnLnEysok0+ezjM6EBLldGqKIJYuDRhOQEJ'.
'Oih8X9Q0xmcXNjlCofBJgn78wxVz7L2YWf8tPPz1hnfjbjzfxN'.
'qVwutq2etZXUQSXikcXGIgUiUkJSDIQMJgYGJsaB3c7b1qQ4GZ'.
'xSkdGZIwMeNLfK6uezMnvJK3pLxeVixfvMsyVjSNSO6IV9adPG'.
'AArkEEz8oUkFmBjYGO80qfd6pCWIayD59wIKcsjcKqufn7JO/S'.
'xfyi+5c24pey5rZ09mJRNkiDdT/tzbkBr3SYkpMYpgEaIJSYhI'.
'kSOY1GhilAQk5ntDIojxCZ/kf87Pl85xbuWEnLiUy+cW3NNuJX'.
'MmY5meKf6mT7wZS+THdOjxlG06tIlIOMZxchSxcFFEGAwAGGME'.
'jwyZYSnWL3cXWiIUbUI6hO/vxXuFOV84ycmlBWthNeflTjuzTi'.
'lzJmM5s46Ej0J63/ZoPmoy6PYxtYVNhmfs0mbAND1mmKVMBY1L'.
'mxA1LN7WgXQbCApNhKJHRIM+DQbv7yQGhjnJ5NgFuXBuxpu5mD'.
'udm3LPuY7pmZLUE6L1SIJaIPFuDAqyw9lnwDYv6NFHkWJh4ZDB'.
'wCBFD3uMxsTAwcBAiElpE/KcPg36dIiOvpsRxDCyhmlP2YY9ZU'.
'v8NMb/1id+FGO0DTztkSXLOONUqeITsMkW2zwnJEIDFhYGx+A1'.
'kwK4mASkvKDPc3p0iYhRRwYUhZLUTyV6Eu0t4s1Y4kcx6W6KaM'.
'EZThcXH59RRhGEgIAddnBwNEBKqqpUtWBIF22YDIhJsbEkJqFN'.
'qLtERHs7GnUkwISEQAf0uj30bY39PzbiC6qrDu2cExJ69Nhhhz'.
'59UlIUipCQOnVi4sjG7ubJBy6um0C+he/0iDHQKIQERYyKFLqr'.
'SI/W6kJCnvOcrWSLSquC1/Jw9Ks3R0FQKHr0uMc9bnCDGjX69A'.
'H0XlcJkibN5jOe/alCZStHbjJL9lSMLkXExvCXRiDV6GZEeGeX'.
'3TvvBVQoEjfBL/v0rT75Th7VU5C8gktI6NLlMY+5yU3WWGODDf'.
'r098tHpNFNH7/2lKdXXdz7efLzVaqJIBOCmK8AJUlI6g0aV+9y'.
'9+p7AR3bMQpTBWPy7yeN6fy0jNwewfpvC9Xe+3kFoUuXe9zj5n'.
'BusEGHjh6GIAGawC2FWuvSvbbF1maFylZAsC1ISZADBiVNSJrP'.
'eX73MY//skHP85z5+fnSxQsXj//4n39cmnPn7LbZlsajBmEnBL'.
'1nuEGDG9x4aa5Ldz+h0RCuBqwBv1Wo+7vs9r7n++0MmYeAM+zB'.
'+61EK1QUEnbbtN+9Bh3Hsebn54u//PdfLq9eWl2ZnZ1dSnaSwu'.
'Pin40b9g3doKE0WoNIl65xj3v75njd3BBubQi6ExKmDWkMRKSl'.
'tSbVKQcMao1Go5Ugb0+x53nOyZMnSysrKydXLq1cWlxa/McgCB'.
'Yev3hU+GPrD3I5/q94k3pXYQY58q6B5Bs0HB//neaGx00gyWaz'.
'VCoV7bquCoKAnZ0dfN/f03egLGj0m3XQNE1jdnY2/+WXXy6trq'.
'6uzP3oR5eCIFi4detW5feXL1vr679Let37zVB3/mQytjXJwmSB'.
'wikHp9ShY0RESqObwPrr5oBERKhUKly4cIFqtUq9XufmzZtsbW'.
'2hXvuDwTTNtxZq8TyvsLy8vLS4uLgahOHphw8elL69fNlc++qr'.
'uFOrNXPddm1cczVL5f5P+Lv5MuOJgTGxwYbZpZsCdeAq8M1Bcw'.
'CGYeC6LtVqlRMnTjAyMkKn0yGXyx0N0LZt03Ec1zCMSrfXO37v'.
'zp3S769csb+/ciXt1mrNdHf3ltZ6Lca8ZpJsduhtCdb2gEFJoQ'.
'xADYHuHDS3f32lFEEQUK/XGRkZoVAocP78eZaXl9FaI/Jq25Uk'.
'yWHAYrHozM/PlxYWFibTND32sFbLXrtyxVz76qukXas1M61WTW'.
'm99gx+20TdN9jqtfjP7QzOwwYNp037Zd0DukDnIByA1pqdnR2+'.
'++472u02Z8+eZWJiAsMwDsEBRNGBzYJpmsaJEyfyX3zxxdLS0t'.
'KlVqu1dP3q1cLta9ekU6u1dat1J9b6Sk9kraV1rYXegW7apDYw'.
'kFY6fPc4MNTw88bwfZ/NzU2UUnieRxAEiAiGcXiXfcigiIjruo'.
'VyubxkWdbK7fX1xWvffFMInjzBM82uMT5+p++6V1UUrSe7u03t'.
'+8lezlKt3gHyl0aSJDQaDa5fv876+vo+w6FzDq1BpZRsb2/bly'.
'9f9vL5/Njdu3fzG0+eMJHNxsfn532vXN5NPG/7abPZal6/Hvfe'.
'kroPHfsm98f7AHW9Xo+//vrrlmVZm71+37QNw3JnZ9PK4uJGpV'.
'pt4Dh+vLGhsrmcfv1iHzu01m89HjIdCon2fb8TBMHtvYeRUn50'.
'1Oj4vqp3Ok1f5LYSadfr9dQfDN642P/XeF2DA+SBAuA4jkOhUK'.
'BQKESO43S11p3BYBDt7u4y+CtB/i/q7jp1GMiw2AAAAABJRU5E'.
'rkJggg==' ;
 
//==========================================================
// File: ppl_blue.png
//==========================================================
$this->imgdata_large[3][0]= 2284 ;
$this->imgdata_large[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMLFRAiTZAL3gAACHlJREFUeJy9mGtv29YZgJ9zKF'.
'F3y/Q9jh05tuQkarKgbYasde0UBdZgwNou/Vqga/sD9mP2B4a1'.
'BbZ9atFPxb5sqOtmXbI19bqsluPYiR3HN90vFEWRZx/IJI5zqa'.
'x0OwBBSgR5Hj7v+55zSEFXTUgIJyA9C6/9RsjMjAyFIxxJCDc7'.
'iBqKgyZACGg3G2x9+xXf/fG33P3mC9qNKsp1O+1JdkEnQTdgIO'.
'ttCSMUi8gj072MnugllAyB9G8rBGi6RsToJTF6iuRoFi1kHKZf'.
'7fB8Iggj0/Dy23D2dakNTR3JDsXPvzstxmZGRMER1EwHhQAEgE'.
'CLhIkPD6InY9S3djGLJVBtQP1Qb4HDAyoJYQOOZkPx49nhTH9i'.
'7MUBGT7egxkJgd70wZS/CUkoZtA/fRoE1DZ2ACiv52ibReCp4e'.
'7CIEHomxDiuVdGTqUnf/ZeOjR8fpiVXZul5ZrY3bWwbdcLr/dA'.
'AAIpAwQjUWIjQ+g9HZvswiCgBVF9/SI6OSLGzo0i+oLi6+Utbq'.
'+bKEftgwOE/0Ohocf66M+cBjo22U2RQLIHMhmYnvaOpR9S8bSU'.
'UqCURGpRkuMZMm9cIvPGJZLj0yBjT2LprkiSkykx9cuXIhOnUs'.
'm+QNC2XdG02ggBTcvFabsPWwTPpBAChSCgh4kYBpoeplWp47Qs'.
'7EYDt21xINzd5GCAxLExRl89Z+nHjpbKMmjbmkgfDzI0JEW53K'.
'Jaa6NcAOEX8v52uJzsBlAS6u0hcnTIccPRqhWPCUcLD+s1EaUp'.
'HCEhEMCyHNpt9SjgIU12A6iw6xb123vYhaaKjB9tlgMD5X+uBp'.
'zdkpg6azA8EaNQtKlVba+Xez4eCntnJrsDdFsW5nYFpxlFN846'.
'DXe8utkM4mhi+EgQmjYbS2WqexZKk6BpjwJ2YlK5VjeA3pNDiH'.
'YjRWPzPE7tmBo8EWwGhkXx+z3uXL7D3rU97LIF8RBEAl6lK/Uo'.
'6JNM1rZ2aTcr3eUgIQOGTgbdwXMGyRejenLYTvQGbAdRuetSud'.
'OivVuFZgtCEgICghICnZoMhmlVTPR49LCAEkQUhk/B7KXe0MWf'.
'nxj8xVR/cDheK14WZmtVMJSBnlGoN6FmQq0FLfdwJgORKPHRo/'.
'Snzx4G0F/FjJ4KiOdmjPCrrx8bffnMybMv9MQGNG3rzlVqtR1B'.
'sh/CYXCD4Aag1oCW7ZnUOjSp6WFi/QNEB8Y7BfTNjZyCmUvJ0I'.
'XXT47MTp98Ybon9VZCk8cVazfqlNargsY34G7ByAlIjkHd9CCr'.
'LbBdiHViUgiECuDKYCdz8b2cywREdiYZOj8zNnLuzOTzx6ODp+'.
'OaGaqwVzBFqz0Idhz2loE7YEwBLaAJLQcKbW8qjAcBF5Jh0AMP'.
'IOHe6kxgtb3UMO2OxkF//ffK28nQqxfvm3szrtnDVa799Qb/+v'.
'NtsbNSpm3tAv8B+w7Ub0FhAyoBcMPec9oK6raXk48ziQBXQcmC'.
'pT3YqHa0mpEBkTR6wz/Jjo2cy04+fzwxdDquNfQKO7sFUbpu0c'.
'wp3JoAYsA42Bbkl4GCryUNDEM7Avm6Z/CgSYBWG8pNuFuDu1Wo'.
'tjoxKIJGeHIiM/jmK9NnX5ycuJQMtUcqXPvLDTa+qIie4hAJ1U'.
'vdrmO2HaDfB931twJgAn1A4lGT96obPHPLBbhVgUoTHHWo9aAA'.
'JVAKpyKEmQNzWRENAsL18ycKjAFN/9gCNvzLB/390MMmE7pnDi'.
'Bvwt0K5Jv3O+0oB22nJ1Vvjb/UMhOpcKknqN1OiMB2DNHU2G5s'.
'sVndpGJVcZXjX1IAlvw9PmhRQcOFPhsSDkiBrQR1G7brgs0a7D'.
'ag3FK4rguqBXarI4Nt1SJv5gls7TEWtJDRBO2GwnIs8maevFnA'.
'Gx6awLZvzeTBu4kFbLigijC47pscpx0xyDfkvtUEnlarCDtrUC'.
't2HGIhvPHVdVwqjTIrxRU2a5uUrYoP0QZ2gMvACl7+3V/LuKDq'.
'sJuDy597516+CEezIHXv7vcgXQu2l+Bvn8He9Y4AE4kgk5P9DE'.
'R6aFdq5Et5Nit3yTf3m9sBcsAN3+D98c0Fit5JawE25r1zg1Fo'.
'5B8GFD7g+nVYnu8EUEop9XTa0N/9dUbqcphP/rDJzbUClVbpgR'.
'y2fXM3fND95qj75J8AC6BWPINfVSBieK+x+6cS5UCzCLu3oFV9'.
'GqCMx2NGOp2Znpv7aXZudsool3T5J/179sxVlHJ4kGPrP2COBX'.
'/7DmiApWCjxIMXpYNznYuXM+6TAKWUMppOZzLvv//ery5cuDCT'.
'SqVS336bCwr1JfAPB9r+2KAFwJS+OcETzZHz/7v3etl6ipz77X'.
'GAMh6PG+l0OjM3NzczOzs3k0pNnFlbW43+e/GKtMqrblSsF03V'.
'WHcJA0PjIAzvg9JTze2H67g9DjAwOTmZ+uCDD96anZ2dnZiYmF'.
'5dW41++Lvfa1fnr7qllVK9103mXNTnJgPA+YugsvB3HTaEl+Qs'.
'AZ/yeHPPDCiTyaRx5syZbGoilV1dW00szC9oV+avusuLy0Xd0X'.
'MgFkDM+zkYBZEHV8f7wwKu84zmngQoNU0LaZoWUa4K31y5qX/8'.
'4cfyyvwVN5/L10NOKNeg8UmDxoKF5Vfj1xXAgD0JrgAcvBDfel'.
'a4g4AykUgY6XR6emJiIru2ttZXq9S0K19eUcuLy8WQE8o5OAsN'.
'Ggsmpl+NpoL1g9X4UBU+C9xDgEKIwNTUVOqdd955M9mbnJ3/cj'.
'6Vu5aTheXCQXNdVeMzAwJSCGEA2XKpnF1cXIzlFnOVhJPIKdR+'.
'c88ctq4AlVKsrKzw0UcfKcC5uXqzXnNqSzb2pwLxOHP/l7Z/BN'.
'eB01LKt4HTrusKvGr8jB+hGn8MQAkYQMrfw4Nq/MFPtf+rdvDb'.
'k8QL+/5Z4Uepxm7bfwHuTAVUWpWaqAAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: ppl_green.png
//==========================================================
$this->imgdata_large[4][0]= 2854 ;
$this->imgdata_large[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMLFQ4hANhluwAACrNJREFUeJytmF1zE1eagJ+3u9'.
'XdkvUty2AbmLEtEzDBgZ0UpDBOalNTUzU3czl7tct/2n+wt3M/'.
'NVM12SSTQQSyW2TA+QAJQogtYYFtyfrqL3WfvWj5g8AEjzfvhS'.
'SXjk8//Zz3Pf3qCMcJAWxMKlT4kH+jwu/FknnJSUItKFHzCrKA'.
'BggBQx5ziz/wn/yBz3hED4/oaJfSjgVoYjJJgTLTZCjohp7IGT'.
'k5aZ4kb+bRTR30Q7djj8f/kpPMUSCFedRL6W8e8qMQNE6S4xpv'.
'c5HrTPFubiJ3ZnlyOXV59rJYU5Z00h1c3d0brxAiUkScRijisk'.
'6XLTyiN3s8HuAJpniXa/q8/pt8Or+0kF8oXJm5YiydWcIpOrJu'.
'rjOQwd54AQwsMpTJYhPSoYuLQ58An/DnBQSdImXO8avsTPbqpc'.
'lLp67OXDVzMznZLGxSs2qyIRu4at8gKHQEC50kE1icxqCAdxST'.
'xjEA44tqaJlERl8uLWvvnX5PHuQfcCdxh5qq0aX76vj4WgWyXO'.
'QiNgBP8IAaddr08X8+wHFmJSQhBbPAZGoSZSt5wQs6qoNC7UEd'.
'4AEoLIQSCaCCy78Dv8Tiv1hjjW1CRj8XIAgEKqDtt9keboMJZa'.
'vMjuzQVd3Xr9prTJo+GF/jKZea95R25Lxs8jg5qFGiwDnOS0mW'.
'NE0rjNRIt3WbklUCA9mV3Zdz8OBT/JfCQLB0SKYVVjGFYSfx/E'.
'26ow4e6uDujlPFQpE0FU6P8qNTHdXJdEdda0qf0itWBVM3pa/3'.
'ccUlIECJet0cAJoeYk5EZCeS5IwEoerSxccJBwRqFFf38QCTaO'.
'TRVFKJm3NTbtLNSyh2IkhIXsvLCesEGNCWdmwyruSD/z9kUlRc'.
'3bqNlSxhJNJ43p5JITrOEis8Qtr0cXEpU/JT/pmO18n2vb42pU'.
'3JnDnHMBqyPlpnoAaxhr2llv1ZUBqEGlqYwDQMsskMOcMgVL3Y'.
'ZOQTHAcQQiIGjHCwCaiovjrv4hbcpKuJJjIcDHm685RGr4GLCx'.
'YHkAcrLoAoDSLBiAQrMkjqybHJCbxgh+7xAC1MpsgzwRwD3qHL'.
'WyTIBdlAa6u2rHfXaew06PV78ZZjAwleNnkolECoH5i090wOcY'.
'+TgwYzFHiPi1zkOkXexeAMASnVU+LiyiA1wFUuaqggACLizeWw'.
'ycMzyssmVYKkbpGyC5T+OUALk2mKLHKWf+ED/az+YW42d66YL+'.
'aNrmEEzQCFEnKw368EgEvcN1m80eTIQIt0TFOjMJHkzNEBBYPp'.
'sblf8QHzrORO5JaWZ5ZLl6cuJyyxpNPv4PZdoT+GyIxBfI5uUg'.
'eJMCwP2/bIHO1JEudcgUUWOceKNq99mCvnzs5PzRcuTV4y5mRO'.
'SMIjo47z5S7a94oQCNKgJsZwO7D/IDNg3/LLhRNXt4JohBb4aG'.
'82GLdXcf93mQ+Y43r2RHZp+cRy6cqJK4l8MS+tdItaqiYtc0Mm'.
'QpfJARh98HYh9IiXVcaAo58wGb+LBAjbSPgCOcoSa0wzxXtc08'.
'/pv8mfyL+9MLVQvDJ1JVHJV6SZbFI1qtTsB+KlehRtRTGE8Afo'.
'P4DRcAxiEudhAHjjzz+ubgX4oHowakHQOlqzICQwyVPITGVOXi'.
'xfLF6aumzmczl5lHzMff2+fCdPaGttEkXoLQAO9B7C6EugPYby'.
'gVPjGXc5eIbNAJPjGwiAbaAJUQv8wVG7GROkJFpyOqn/ovgLba'.
'44L0+sDaraXb6jzq7aBQWjBOyUoHcaopOgmaA3IRyNDZnA1HjO'.
'HSBkr7eEFDAEngHrQCf+/s2A8cSiSkqcKUeeTjwFy2Jd78t3+L'.
'TR4itIiBLwLQhzkJyB5Cx4HXDaENVQCBAQcRqFIHTRaBIvuYXg'.
'AdsouuNxEL0ZUBHnSQp66R73zYfUtQ6OytKT8RckQAJQoLtgO5'.
'BJgj0D/WfgdyHaAHx8THoUcbGx8ciwhUl3bDEiToURPooeI7pH'.
'MziK9Yd9nU5a6GgKjOH41vsgI4hAcyC5AZkapF+AoYNrjjsuhx'.
'FbtPmeB5ykyQQzTPAWAQWC8S9oAI0QRRuPb9jkmyMZNAOTklvC'.
'GGYZaFkGmkVAh8h4DtKFMIBunG+pB5B5AIkGBDsQ+qBiL20caj'.
'zhJknq5KlgMkLjJHJos4kYEbFJi5vc5eYbATVN02bNWe19+32t'.
'aJWlFm3wbf8Rz5NbDFJdlOFBF/g7cBf0JkrbBb+F6j1DOduEkU'.
'8bWCOiSofPWadBnSZDWmgUkEMGhZCINut8S/0NBtPptFlZrBSu'.
'vnt1+ndnflfIp9OJ/279Ubbbd+lP7KBKPoEBsgnqLph/BRzwdS'.
'LnBUFvHcfdpRsGPAGqwMco6jynz+e0SPKYCHMfLX5VKHwcenR+'.
'Igd1XTcqlUr+xn/cePv91fevzy8sLO2OtrOpWkqL7gXKSAVRdh'.
'ZFEmEXoYkwBNqovoc/3GHH3aUR+jwC1oD/AWrANi4hGwyBzqEG'.
'Vvb77Dgi0eT1VZzJZMxKpVJYXV1dXF1dXVm6sPSvruue3Xzcyj'.
'6/syvDzwj0lNazK6Fj5LFCRZouZpBABj6jXouu3+Np6HNvDHaf'.
'g91t74msbMuOJicnSSaTKKUQEUQEpRSO69But1/dB0VEm5uby9'.
'y4cWNpdXX1+sLCworrume//PuXpeqnVeOban0U1PW2kcx+O9L7'.
'Te9sUB4lWFR9SqNtNGcHx+/RDD2+Am4D94CnQA8OjjlEhMnyJC'.
'srK8zOzu7BiYioMAzZ2Njg9u3brwIqpSSXy2WXl5eXLly4sOo4'.
'zoV6vV6oflrVP/7Tx8Hmw1Zb6ydqmpWp7ha8h4O3gjOhzVANmF'.
'XPMNQWvdDnCXCXuHR+APqH4fbCtm2mp6eZn59H13WJuYXRaKSU'.
'UiSTyVcBdV3XDcOwRaTU7/en19bWCn/79G+JL/76RbhZ22y7u+'.
'6ahl71nPDz/nO17m7wAxlabFOihy4+DvAcqAMbPzZ3OFzX5dmz'.
'Z2iahoiosUUVhiGNRgPHcV4GzGQy5uLiYuH8+fMzo9FoslarJW'.
'9+elP75E+fBJu1zY7qqpqBUW3T/niohnVvy+1zm5aVtp+WE2XT'.
'nrHFzbjh1tYLz3XdPjD4R3BKKba2tqhWq4dzUO3noBPn4H5PKy'.
'LaO++8U7hx48byhQsXVne7u6tf3/v64t3P7mbq9+odt+OuaWi3'.
'PLxbW2ytubjbQCgiMnt6VlaurWgz0zM0m02q1WrUaDSUUuqI56'.
'ivDxE5MCgiYllWtlwuL5mmufLV/a/O/uXPf9Ff1F+80Lv6Yx29'.
'2qHzyZBh3cdvc7gaTZuZkzPh/Py8ACqVSv1/uPZDKXUAGEWRtF'.
'qtxEcffZTL5XLF+2v39fqjeivshA/TpP83JLwzYFBzcA4370Cc'.
'S81nTRBUs9lkOByi1GuOPI4Rh3+26JZlnSkWi781DOPXvV4v3+'.
'/2G0R8kSBxB/jew+tERK+c49m2TblcxrZtXNfl+fPneJ6HZVmU'.
'y2VJJpNyaJ9TSinlOA5bW1u4rntkQA0oAG8D54gb9W3ianxM3A'.
'e/cn73U3Hq1Cm5du2aPjs7a+ztcSIShmE4ajQa6tatWzQajZ+0'.
'fbiKI+It4SvijVUj7kL2qvGfgkskEqTTaZmcnDROnTplJhIJTU'.
'QiwPd9P/Q8T6XTaQzDIAiCfzjP/wFVfszuFqdHXgAAAABJRU5E'.
'rkJggg==' ;
 
 
//==========================================================
// File: pp_red.png
//==========================================================
$this->imgdata_small[0][0]= 384 ;
$this->imgdata_small[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
'B3RJTUUH0wMJFhouFobZrQAAAQ1JREFUeJyV1dFtwyAQBuD/og'.
'xQdYxa8gRY6hJ0jK6QdohMkTEuE5wUj5ERen05IoLvID7Jkn2G'.
'j8MgTMyMXqRlUQBYq9ydmaL2h1cwqD7l30t+L1iwlbYFRegY7I'.
'SHjkEifGg4ww3aBa/l4+9AhxWWr/dLhEunXUGHq6yGniw3QkOw'.
'3jJ7UBd82n/VVAlAtvsfp98lAj2sAJOhU4AeQ7DC1ubVBODWDJ'.
'TtCsEWa6u5M1NeFs1NzgdtuhHGtj+9Q2IDppQUAL6Cyrlz0gDN'.
'ohSMiJCt861672EiAhEhESG3woJ9V9OKTkwRKbdqz4cHmFLSFg'.
's69+LvAZKdeZ/n89uLnd2g0S+gjd5g8zzjH5Y/eLLi+NPEAAAA'.
'AElFTkSuQmCC' ;
 
//==========================================================
// File: pp_orange.png
//==========================================================
$this->imgdata_small[1][0]= 403 ;
$this->imgdata_small[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
'B3RJTUUH0wMJFhwAnApz5AAAASBJREFUeJyN1dFthDAMBuDf7S'.
'3BCm2VCRKpS4QxbhikW6IewzcBqm6Fm6JyH7iEEByCn5AJH38g'.
'BBIRHNUzBAWAGNfe/SrUGv92CtNt309BrfFdMGPjvt9CD8Fyml'.
'ZZaDchRgA/59FDMD18pvNoNyHxMnUmgLmPHoJ+CqqfMaNAH22C'.
'fgqKRwR+GRpxGjXBEiuXDBWQhTK3plxijyWWvtKVS5KNG1xM8I'.
'OBr7geV1WupDqpmTAPKjCqLhxk/z0PImQmjKrAuI6vMXlhFroD'.
'vfdqITXWqg2YMSJEAFcReoag6UXU2DzPG8w5t09YYsAyLWvHrL'.
'HUy6D3XmvMAAhAay8kAJpBosX4vt0G4+4Jam6s6Rz1fgFG0ncA'.
'f3XfOQcA+Acv5IUSdQw9hgAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: pp_pink.png
//==========================================================
$this->imgdata_small[2][0]= 419 ;
$this->imgdata_small[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
'B3RJTUUH0wMJFhsQzvz1RwAAATBJREFUeJyd1MFthDAQheF/oi'.
'gF+JYWQKICkCJRA1vGtrDbxFbhGvY0HVjCLeS2BeTiHFgTB2wg'.
'eRISstCnmcG2qCpbuXf3ADBQzWsPfZfS9y9HsEu4/Fo33Wf4Fx'.
'gxL3a1XkI3wbTNXHLoboVeLFUYDqObYBy+Fw/Uh9DdCmtOwIjF'.
'YvG76CZoOhNGRmpO8zz30CJoOhMAqlDxFzQLppgXj2XaNlP7FF'.
'GLL7ccMYCBgZERgCvXLBrfi2DEclmiKZwFY4tp6sW26bVfnede'.
'e5Hc5dC2bUgrXGKqWrwcXnNYDjmCrcCIiQgDcFYV05kQ8SXmnB'.
'NgPiVN06wrTDGAhz5EWY/FOccTk+cTnHM/YNu2YYllgFxCWuUM'.
'ikzGx+2Gc+4N+CoJW8n+5a2UKm2aBoBvGA6L7wfl8aoAAAAASU'.
'VORK5CYII=' ;
 
 
//==========================================================
// File: pp_blue.png
//==========================================================
$this->imgdata_small[3][0]= 883 ;
$this->imgdata_small[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAACi1'.
'BMVEX///8AAAAAADMAAGYAAJkAAMwAAP8zAAAzADMzAGYzAJkz'.
'AMwzAP9mAABmADNmAGZmAJlmAMxmAP+ZAACZADOZAGaZAJmZAM'.
'yZAP/MAADMADPMAGbMAJnMAMzMAP//AAD/ADP/AGb/AJn/AMz/'.
'AP8AMwAAMzMAM2YAM5kAM8wAM/8zMwAzMzMzM2YzM5kzM8wzM/'.
'9mMwBmMzNmM2ZmM5lmM8xmM/+ZMwCZMzOZM2aZM5mZM8yZM//M'.
'MwDMMzPMM2bMM5nMM8zMM///MwD/MzP/M2b/M5n/M8z/M/8AZg'.
'AAZjMAZmYAZpkAZswAZv8zZgAzZjMzZmYzZpkzZswzZv9mZgBm'.
'ZjNmZmZmZplmZsxmZv+ZZgCZZjOZZmaZZpmZZsyZZv/MZgDMZj'.
'PMZmbMZpnMZszMZv//ZgD/ZjP/Zmb/Zpn/Zsz/Zv8AmQAAmTMA'.
'mWYAmZkAmcwAmf8zmQAzmTMzmWYzmZkzmcwzmf9mmQBmmTNmmW'.
'ZmmZlmmcxmmf+ZmQCZmTOZmWaZmZmZmcyZmf/MmQDMmTPMmWbM'.
'mZnMmczMmf//mQD/mTP/mWb/mZn/mcz/mf8AzAAAzDMAzGYAzJ'.
'kAzMwAzP8zzAAzzDMzzGYzzJkzzMwzzP9mzABmzDNmzGZmzJlm'.
'zMxmzP+ZzACZzDOZzGaZzJmZzMyZzP/MzADMzDPMzGbMzJnMzM'.
'zMzP//zAD/zDP/zGb/zJn/zMz/zP8A/wAA/zMA/2YA/5kA/8wA'.
'//8z/wAz/zMz/2Yz/5kz/8wz//9m/wBm/zNm/2Zm/5lm/8xm//'.
'+Z/wCZ/zOZ/2aZ/5mZ/8yZ///M/wDM/zPM/2bM/5nM/8zM////'.
'/wD//zP//2b//5n//8z///9jJVUgAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
'RQfTAwkWGTNerea3AAAAYUlEQVR4nHXNwQ3AIAxDUUfyoROxRZ'.
'icARin0EBTIP3Hp1gBRqSqYo0seqjZpnngojlWBir5+b8o06lM'.
'ha5uFKEpDZulV8l52axhVzqaCdxQp32qVSSwC1wN3fYiw7b76w'.
'bN4SMue4/KbwAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: pp_green.png
//==========================================================
$this->imgdata_small[4][0]= 447 ;
$this->imgdata_small[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
'B3RJTUUH0wMJFhkLdq9eKQAAAUxJREFUeJyN1LFVwzAQxvH/8f'.
'IeDS0FLKABlN6eIwPYAzCHB0gWYI2jj+i1ABUTQN4TRSQ7iiWZ'.
'qxLn9Mt9ydmiqrSq930AYFiu6YdKrf/hP1gYQn6960PxwBaYMG'.
'E9UA3dBFtVQjdBOQmBakLennK0CapRwbZRZ3N0O/IeEsqp3HKL'.
'Smtt5pUZgTPg4gdDud+6xoS97wM2rsxxmRSoTgoVcMZsXJkBho'.
'SmKqCuOuEtls6nmGMFPTUmxBKx/MeyNfQGLoOOiC2ddsxb1Kzv'.
'ZzUqu5IXbGDvBJf+hDisi77qFSuhq7Xpuu66TyJLRGbsXVUPxV'.
'SxsgkzDMt0mKT3/RcjL8C5hHnvJToXY0xYRZ4xnVKsV/S+a8YA'.
'AvCb3s9g13UhYj+TTo93B3fApRV1FVlEAD6H42DjN9/WvzDYuJ'.
'dL5b1/ji+/IX8EGWP4AwRii8PdFHTqAAAAAElFTkSuQmCC' ;
}
}
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_canvtools.php
New file
0,0 → 1,517
<?php
/*=======================================================================
// File: JPGRAPH_CANVTOOLS.PHP
// Description: Some utilities for text and shape drawing on a canvas
// Created: 2002-08-23
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_canvtools.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
DEFINE('CORNER_TOPLEFT',0);
DEFINE('CORNER_TOPRIGHT',1);
DEFINE('CORNER_BOTTOMRIGHT',2);
DEFINE('CORNER_BOTTOMLEFT',3);
 
 
//===================================================
// CLASS CanvasScale
// Description: Define a scale for canvas so we
// can abstract away with absolute pixels
//===================================================
class CanvasScale {
var $g;
var $w,$h;
var $ixmin=0,$ixmax=10,$iymin=0,$iymax=10;
 
function CanvasScale(&$graph,$xmin=0,$xmax=10,$ymin=0,$ymax=10) {
$this->g = &$graph;
$this->w = $graph->img->width;
$this->h = $graph->img->height;
$this->ixmin = $xmin;
$this->ixmax = $xmax;
$this->iymin = $ymin;
$this->iymax = $ymax;
}
function Set($xmin=0,$xmax=10,$ymin=0,$ymax=10) {
$this->ixmin = $xmin;
$this->ixmax = $xmax;
$this->iymin = $ymin;
$this->iymax = $ymax;
}
 
function Translate($x,$y) {
$xp = round(($x-$this->ixmin)/($this->ixmax - $this->ixmin) * $this->w);
$yp = round(($y-$this->iymin)/($this->iymax - $this->iymin) * $this->h);
return array($xp,$yp);
}
 
function TranslateX($x) {
$xp = round(($x-$this->ixmin)/($this->ixmax - $this->ixmin) * $this->w);
return $xp;
}
 
function TranslateY($y) {
$yp = round(($y-$this->iymin)/($this->iymax - $this->iymin) * $this->h);
return $yp;
}
 
}
 
 
//===================================================
// CLASS Shape
// Description: Methods to draw shapes on canvas
//===================================================
class Shape {
var $img,$scale;
 
function Shape(&$aGraph,&$scale) {
$this->img = &$aGraph->img;
$this->img->SetColor('black');
$this->scale = &$scale;
}
 
function SetColor($aColor) {
$this->img->SetColor($aColor);
}
 
function Line($x1,$y1,$x2,$y2) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
list($x2,$y2) = $this->scale->Translate($x2,$y2);
$this->img->Line($x1,$y1,$x2,$y2);
}
 
function Polygon($p,$aClosed=false) {
$n=count($p);
for($i=0; $i < $n; $i+=2 ) {
$p[$i] = $this->scale->TranslateX($p[$i]);
$p[$i+1] = $this->scale->TranslateY($p[$i+1]);
}
$this->img->Polygon($p,$aClosed);
}
 
function FilledPolygon($p) {
$n=count($p);
for($i=0; $i < $n; $i+=2 ) {
$p[$i] = $this->scale->TranslateX($p[$i]);
$p[$i+1] = $this->scale->TranslateY($p[$i+1]);
}
$this->img->FilledPolygon($p);
}
 
// Draw a bezier curve with defining points in the $aPnts array
// using $aSteps steps.
// 0=x0, 1=y0
// 2=x1, 3=y1
// 4=x2, 5=y2
// 6=x3, 7=y3
function Bezier($p,$aSteps=40) {
$x0 = $p[0];
$y0 = $p[1];
// Calculate coefficients
$cx = 3*($p[2]-$p[0]);
$bx = 3*($p[4]-$p[2])-$cx;
$ax = $p[6]-$p[0]-$cx-$bx;
$cy = 3*($p[3]-$p[1]);
$by = 3*($p[5]-$p[3])-$cy;
$ay = $p[7]-$p[1]-$cy-$by;
 
// Step size
$delta = 1.0/$aSteps;
 
$x_old = $x0;
$y_old = $y0;
for($t=$delta; $t<=1.0; $t+=$delta) {
$tt = $t*$t; $ttt=$tt*$t;
$x = $ax*$ttt + $bx*$tt + $cx*$t + $x0;
$y = $ay*$ttt + $by*$tt + $cy*$t + $y0;
$this->Line($x_old,$y_old,$x,$y);
$x_old = $x;
$y_old = $y;
}
$this->Line($x_old,$y_old,$p[6],$p[7]);
}
 
function Rectangle($x1,$y1,$x2,$y2) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
list($x2,$y2) = $this->scale->Translate($x2,$y2);
$this->img->Rectangle($x1,$y1,$x2,$y2);
}
 
function FilledRectangle($x1,$y1,$x2,$y2) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
list($x2,$y2) = $this->scale->Translate($x2,$y2);
$this->img->FilledRectangle($x1,$y1,$x2,$y2);
}
function Circle($x1,$y1,$r) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
if( $r >= 0 )
$r = $this->scale->TranslateX($r);
else
$r = -$r;
$this->img->Circle($x1,$y1,$r);
}
 
function FilledCircle($x1,$y1,$r) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
if( $r >= 0 )
$r = $this->scale->TranslateX($r);
else
$r = -$r;
$this->img->FilledCircle($x1,$y1,$r);
}
 
function RoundedRectangle($x1,$y1,$x2,$y2,$r=null) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
list($x2,$y2) = $this->scale->Translate($x2,$y2);
 
if( $r == null )
$r = 5;
elseif( $r >= 0 )
$r = $this->scale->TranslateX($r);
else
$r = -$r;
$this->img->RoundedRectangle($x1,$y1,$x2,$y2,$r);
}
 
function FilledRoundedRectangle($x1,$y1,$x2,$y2,$r=null) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
list($x2,$y2) = $this->scale->Translate($x2,$y2);
 
if( $r == null )
$r = 5;
elseif( $r > 0 )
$r = $this->scale->TranslateX($r);
else
$r = -$r;
$this->img->FilledRoundedRectangle($x1,$y1,$x2,$y2,$r);
}
 
function ShadowRectangle($x1,$y1,$x2,$y2,$fcolor=false,$shadow_width=null,$shadow_color=array(102,102,102)) {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
list($x2,$y2) = $this->scale->Translate($x2,$y2);
if( $shadow_width == null )
$shadow_width=4;
else
$shadow_width=$this->scale->TranslateX($shadow_width);
$this->img->ShadowRectangle($x1,$y1,$x2,$y2,$fcolor,$shadow_width,$shadow_color);
}
 
function SetTextAlign($halign,$valign="bottom") {
$this->img->SetTextAlign($halign,$valign="bottom");
}
 
function StrokeText($x1,$y1,$txt,$dir=0,$paragraph_align="left") {
list($x1,$y1) = $this->scale->Translate($x1,$y1);
$this->img->StrokeText($x1,$y1,$txt,$dir,$paragraph_align);
}
 
// A rounded rectangle where one of the corner has been moved "into" the
// rectangle 'iw' width and 'ih' height. Corners:
// 0=Top left, 1=top right, 2=bottom right, 3=bottom left
function IndentedRectangle($xt,$yt,$w,$h,$iw=0,$ih=0,$aCorner=3,$aFillColor="",$r=4) {
list($xt,$yt) = $this->scale->Translate($xt,$yt);
list($w,$h) = $this->scale->Translate($w,$h);
list($iw,$ih) = $this->scale->Translate($iw,$ih);
$xr = $xt + $w - 0;
$yl = $yt + $h - 0;
 
switch( $aCorner ) {
case 0: // Upper left
// Bottom line, left & right arc
$this->img->Line($xt+$r,$yl,$xr-$r,$yl);
$this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
$this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
 
// Right line, Top right arc
$this->img->Line($xr,$yt+$r,$xr,$yl-$r);
$this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
 
// Top line, Top left arc
$this->img->Line($xt+$iw+$r,$yt,$xr-$r,$yt);
$this->img->Arc($xt+$iw+$r,$yt+$r,$r*2,$r*2,180,270);
 
// Left line
$this->img->Line($xt,$yt+$ih+$r,$xt,$yl-$r);
 
// Indent horizontal, Lower left arc
$this->img->Line($xt+$r,$yt+$ih,$xt+$iw-$r,$yt+$ih);
$this->img->Arc($xt+$r,$yt+$ih+$r,$r*2,$r*2,180,270);
 
// Indent vertical, Indent arc
$this->img->Line($xt+$iw,$yt+$r,$xt+$iw,$yt+$ih-$r);
$this->img->Arc($xt+$iw-$r,$yt+$ih-$r,$r*2,$r*2,0,90);
 
if( $aFillColor != '' ) {
$bc = $this->img->current_color_name;
$this->img->PushColor($aFillColor);
$this->img->FillToBorder($xr-$r,$yl-$r,$bc);
$this->img->PopColor();
}
 
break;
 
case 1: // Upper right
 
// Bottom line, left & right arc
$this->img->Line($xt+$r,$yl,$xr-$r,$yl);
$this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
$this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
 
// Left line, Top left arc
$this->img->Line($xt,$yt+$r,$xt,$yl-$r);
$this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
 
// Top line, Top right arc
$this->img->Line($xt+$r,$yt,$xr-$iw-$r,$yt);
$this->img->Arc($xr-$iw-$r,$yt+$r,$r*2,$r*2,270,360);
 
// Right line
$this->img->Line($xr,$yt+$ih+$r,$xr,$yl-$r);
 
// Indent horizontal, Lower right arc
$this->img->Line($xr-$iw+$r,$yt+$ih,$xr-$r,$yt+$ih);
$this->img->Arc($xr-$r,$yt+$ih+$r,$r*2,$r*2,270,360);
 
// Indent vertical, Indent arc
$this->img->Line($xr-$iw,$yt+$r,$xr-$iw,$yt+$ih-$r);
$this->img->Arc($xr-$iw+$r,$yt+$ih-$r,$r*2,$r*2,90,180);
 
if( $aFillColor != '' ) {
$bc = $this->img->current_color_name;
$this->img->PushColor($aFillColor);
$this->img->FillToBorder($xt+$r,$yl-$r,$bc);
$this->img->PopColor();
}
 
break;
 
case 2: // Lower right
// Top line, Top left & Top right arc
$this->img->Line($xt+$r,$yt,$xr-$r,$yt);
$this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
$this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
 
// Left line, Bottom left arc
$this->img->Line($xt,$yt+$r,$xt,$yl-$r);
$this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
 
// Bottom line, Bottom right arc
$this->img->Line($xt+$r,$yl,$xr-$iw-$r,$yl);
$this->img->Arc($xr-$iw-$r,$yl-$r,$r*2,$r*2,0,90);
 
// Right line
$this->img->Line($xr,$yt+$r,$xr,$yl-$ih-$r);
// Indent horizontal, Lower right arc
$this->img->Line($xr-$r,$yl-$ih,$xr-$iw+$r,$yl-$ih);
$this->img->Arc($xr-$r,$yl-$ih-$r,$r*2,$r*2,0,90);
 
// Indent vertical, Indent arc
$this->img->Line($xr-$iw,$yl-$r,$xr-$iw,$yl-$ih+$r);
$this->img->Arc($xr-$iw+$r,$yl-$ih+$r,$r*2,$r*2,180,270);
 
if( $aFillColor != '' ) {
$bc = $this->img->current_color_name;
$this->img->PushColor($aFillColor);
$this->img->FillToBorder($xt+$r,$yt+$r,$bc);
$this->img->PopColor();
}
 
break;
 
case 3: // Lower left
// Top line, Top left & Top right arc
$this->img->Line($xt+$r,$yt,$xr-$r,$yt);
$this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
$this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
 
// Right line, Bottom right arc
$this->img->Line($xr,$yt+$r,$xr,$yl-$r);
$this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
 
// Bottom line, Bottom left arc
$this->img->Line($xt+$iw+$r,$yl,$xr-$r,$yl);
$this->img->Arc($xt+$iw+$r,$yl-$r,$r*2,$r*2,90,180);
 
// Left line
$this->img->Line($xt,$yt+$r,$xt,$yl-$ih-$r);
// Indent horizontal, Lower left arc
$this->img->Line($xt+$r,$yl-$ih,$xt+$iw-$r,$yl-$ih);
$this->img->Arc($xt+$r,$yl-$ih-$r,$r*2,$r*2,90,180);
 
// Indent vertical, Indent arc
$this->img->Line($xt+$iw,$yl-$ih+$r,$xt+$iw,$yl-$r);
$this->img->Arc($xt+$iw-$r,$yl-$ih+$r,$r*2,$r*2,270,360);
 
if( $aFillColor != '' ) {
$bc = $this->img->current_color_name;
$this->img->PushColor($aFillColor);
$this->img->FillToBorder($xr-$r,$yt+$r,$bc);
$this->img->PopColor();
}
 
break;
}
}
}
 
 
//===================================================
// CLASS RectangleText
// Description: Draws a text paragraph inside a
// rounded, possible filled, rectangle.
//===================================================
class CanvasRectangleText {
var $ix,$iy,$iw,$ih,$ir=4;
var $iTxt,$iColor='black',$iFillColor='',$iFontColor='black';
var $iParaAlign='center';
var $iAutoBoxMargin=5;
var $iShadowWidth=3,$iShadowColor='';
 
function CanvasRectangleText($aTxt='',$xl=0,$yt=0,$w=0,$h=0) {
$this->iTxt = new Text($aTxt);
$this->ix = $xl;
$this->iy = $yt;
$this->iw = $w;
$this->ih = $h;
}
function SetShadow($aColor='gray',$aWidth=3) {
$this->iShadowColor = $aColor;
$this->iShadowWidth = $aWidth;
}
 
function SetFont($FontFam,$aFontStyle,$aFontSize=12) {
$this->iTxt->SetFont($FontFam,$aFontStyle,$aFontSize);
}
 
function SetTxt($aTxt) {
$this->iTxt->Set($aTxt);
}
 
function ParagraphAlign($aParaAlign) {
$this->iParaAlign = $aParaAlign;
}
 
function SetFillColor($aFillColor) {
$this->iFillColor = $aFillColor;
}
 
function SetAutoMargin($aMargin) {
$this->iAutoBoxMargin=$aMargin;
}
 
function SetColor($aColor) {
$this->iColor = $aColor;
}
 
function SetFontColor($aColor) {
$this->iFontColor = $aColor;
}
 
function SetPos($xl=0,$yt=0,$w=0,$h=0) {
$this->ix = $xl;
$this->iy = $yt;
$this->iw = $w;
$this->ih = $h;
}
 
function Pos($xl=0,$yt=0,$w=0,$h=0) {
$this->ix = $xl;
$this->iy = $yt;
$this->iw = $w;
$this->ih = $h;
}
 
function Set($aTxt,$xl,$yt,$w=0,$h=0) {
$this->iTxt->Set($aTxt);
$this->ix = $xl;
$this->iy = $yt;
$this->iw = $w;
$this->ih = $h;
}
 
function SetCornerRadius($aRad=5) {
$this->ir = $aRad;
}
 
function Stroke($aImg,$scale) {
// If coordinates are specifed as negative this means we should
// treat them as abolsute (pixels) coordinates
if( $this->ix > 0 ) {
$this->ix = $scale->TranslateX($this->ix) ;
}
else {
$this->ix = -$this->ix;
}
 
if( $this->iy > 0 ) {
$this->iy = $scale->TranslateY($this->iy) ;
}
else {
$this->iy = -$this->iy;
}
list($this->iw,$this->ih) = $scale->Translate($this->iw,$this->ih) ;
 
if( $this->iw == 0 )
$this->iw = round($this->iTxt->GetWidth($aImg) + $this->iAutoBoxMargin);
if( $this->ih == 0 ) {
$this->ih = round($this->iTxt->GetTextHeight($aImg) + $this->iAutoBoxMargin);
}
 
if( $this->iShadowColor != '' ) {
$aImg->PushColor($this->iShadowColor);
$aImg->FilledRoundedRectangle($this->ix+$this->iShadowWidth,
$this->iy+$this->iShadowWidth,
$this->ix+$this->iw-1+$this->iShadowWidth,
$this->iy+$this->ih-1+$this->iShadowWidth,
$this->ir);
$aImg->PopColor();
}
 
if( $this->iFillColor != '' ) {
$aImg->PushColor($this->iFillColor);
$aImg->FilledRoundedRectangle($this->ix,$this->iy,
$this->ix+$this->iw-1,
$this->iy+$this->ih-1,
$this->ir);
$aImg->PopColor();
}
 
if( $this->iColor != '' ) {
$aImg->PushColor($this->iColor);
$aImg->RoundedRectangle($this->ix,$this->iy,
$this->ix+$this->iw-1,
$this->iy+$this->ih-1,
$this->ir);
$aImg->PopColor();
}
$this->iTxt->Align('center','center');
$this->iTxt->ParagraphAlign($this->iParaAlign);
$this->iTxt->SetColor($this->iFontColor);
$this->iTxt->Stroke($aImg, $this->ix+$this->iw/2, $this->iy+$this->ih/2);
 
return array($this->iw, $this->ih);
 
}
 
}
 
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_regstat.php
New file
0,0 → 1,105
<?php
/*=======================================================================
// File: JPGRAPH_REGSTAT.PHP
// Description: Regression and statistical analysis helper classes
// Created: 2002-12-01
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_regstat.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2002 Johan Persson
//========================================================================
*/
 
//------------------------------------------------------------------------
// CLASS Spline
// Create a new data array from an existing data array but with more points.
// The new points are interpolated using a cubic spline algorithm
//------------------------------------------------------------------------
class Spline {
// 3:rd degree polynom approximation
 
var $xdata,$ydata; // Data vectors
var $y2; // 2:nd derivate of ydata
var $n=0;
 
function Spline($xdata,$ydata) {
$this->y2 = array();
$this->xdata = $xdata;
$this->ydata = $ydata;
 
$n = count($ydata);
$this->n = $n;
 
// Natural spline 2:derivate == 0 at endpoints
$this->y2[0] = 0.0;
$this->y2[$n-1] = 0.0;
$delta[0] = 0.0;
 
// Calculate 2:nd derivate
for($i=1; $i < $n-1; ++$i) {
$d = ($xdata[$i+1]-$xdata[$i-1]);
if( $d == 0 ) {
JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
}
$s = ($xdata[$i]-$xdata[$i-1])/$d;
$p = $s*$this->y2[$i-1]+2.0;
$this->y2[$i] = ($s-1.0)/$p;
$delta[$i] = ($ydata[$i+1]-$ydata[$i])/($xdata[$i+1]-$xdata[$i]) -
($ydata[$i]-$ydata[$i-1])/($xdata[$i]-$xdata[$i-1]);
$delta[$i] = (6.0*$delta[$i]/($xdata[$i+1]-$xdata[$i-1])-$s*$delta[$i-1])/$p;
}
 
// Backward substitution
for( $j=$n-2; $j >= 0; --$j ) {
$this->y2[$j] = $this->y2[$j]*$this->y2[$j+1] + $delta[$j];
}
}
 
// Return the two new data vectors
function Get($num=50) {
$n = $this->n ;
$step = ($this->xdata[$n-1]-$this->xdata[0]) / ($num-1);
$xnew=array();
$ynew=array();
$xnew[0] = $this->xdata[0];
$ynew[0] = $this->ydata[0];
for( $j=1; $j < $num; ++$j ) {
$xnew[$j] = $xnew[0]+$j*$step;
$ynew[$j] = $this->Interpolate($xnew[$j]);
}
return array($xnew,$ynew);
}
 
// Return a single interpolated Y-value from an x value
function Interpolate($xpoint) {
 
$max = $this->n-1;
$min = 0;
 
// Binary search to find interval
while( $max-$min > 1 ) {
$k = ($max+$min) / 2;
if( $this->xdata[$k] > $xpoint )
$max=$k;
else
$min=$k;
}
 
// Each interval is interpolated by a 3:degree polynom function
$h = $this->xdata[$max]-$this->xdata[$min];
 
if( $h == 0 ) {
JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
}
 
 
$a = ($this->xdata[$max]-$xpoint)/$h;
$b = ($xpoint-$this->xdata[$min])/$h;
return $a*$this->ydata[$min]+$b*$this->ydata[$max]+
(($a*$a*$a-$a)*$this->y2[$min]+($b*$b*$b-$b)*$this->y2[$max])*($h*$h)/6.0;
}
}
 
// EOF
?>
/trunk/api/jpgraph_1.12.2/jpgraph_plotmark.inc
New file
0,0 → 1,452
<?php
//=======================================================================
// File: JPGRAPH_PLOTMARK.PHP
// Description: Class file. Handles plotmarks
// Created: 2003-03-21
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_plotmark.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
 
//========================================================================
// CLASS ImgData
// Description: Base class for all image data classes that contains the
// real image data.
//========================================================================
class ImgData {
var $name = ''; // Each subclass gives a name
var $an = array(); // Data array names
var $colors = array(); // Available colors
var $index = array(); // Index for colors
var $maxidx = 0 ; // Max color index
var $anchor_x=0.5, $anchor_y=0.5 ; // Where is the center of the image
// Create a GD image from the data and return a GD handle
function GetImg($aMark,$aIdx) {
$n = $this->an[$aMark];
if( is_string($aIdx) ) {
if( !in_array($aIdx,$this->colors) ) {
JpGraphError::Raise('This marker "'.($this->name).'" does not exist in color: '.$aIdx);
die();
}
$idx = $this->index[$aIdx];
}
elseif( !is_integer($aIdx) ||
(is_integer($aIdx) && $aIdx > $this->maxidx ) ) {
JpGraphError::Raise('Mark color index too large for marker "'.($this->name).'"');
}
else
$idx = $aIdx ;
return imagecreatefromstring(base64_decode($this->{$n}[$idx][1]));
}
function GetAnchor() {
return array($this->anchor_x,$this->anchor_y);
}
}
 
 
//===================================================
// CLASS PlotMark
// Description: Handles the plot marks in graphs
//===================================================
class PlotMark {
var $title, $show=true;
var $type,$weight=1;
var $color="black", $width=4, $fill_color="blue";
var $yvalue,$xvalue='',$csimtarget,$csimalt,$csimareas;
var $iFormatCallback="";
var $iFormatCallback2="";
var $markimg='',$iScale=1.0;
var $oldfilename='',$iFileName='';
var $imgdata_balls = null;
var $imgdata_diamonds = null;
var $imgdata_squares = null;
var $imgdata_bevels = null;
var $imgdata_stars = null;
var $imgdata_pushpins = null;
 
//--------------
// CONSTRUCTOR
function PlotMark() {
$this->title = new Text();
$this->title->Hide();
$this->csimareas = '';
$this->csimalt = '';
$this->type=-1;
}
//---------------
// PUBLIC METHODS
function SetType($aType,$aFileName='',$aScale=1.0) {
$this->type = $aType;
if( $aType == MARK_IMG && $aFileName=='' ) {
JpGraphError::Raise('A filename must be specified if you set the mark type to MARK_IMG.');
}
$this->iFileName = $aFileName;
$this->iScale = $aScale;
}
function SetCallback($aFunc) {
$this->iFormatCallback = $aFunc;
}
 
function SetCallbackYX($aFunc) {
$this->iFormatCallback2 = $aFunc;
}
function GetType() {
return $this->type;
}
function SetColor($aColor) {
$this->color=$aColor;
}
function SetFillColor($aFillColor) {
$this->fill_color = $aFillColor;
}
 
function SetWeight($aWeight) {
$this->weight = $aWeight;
}
 
// Synonym for SetWidth()
function SetSize($aWidth) {
$this->width=$aWidth;
}
function SetWidth($aWidth) {
$this->width=$aWidth;
}
 
function SetDefaultWidth() {
switch( $this->type ) {
case MARK_CIRCLE:
case MARK_FILLEDCIRCLE:
$this->width=4;
break;
default:
$this->width=7;
}
}
function GetWidth() {
return $this->width;
}
function Hide($aHide=true) {
$this->show = !$aHide;
}
function Show($aShow=true) {
$this->show = $aShow;
}
 
function SetCSIMAltVal($aY,$aX='') {
$this->yvalue=$aY;
$this->xvalue=$aX;
}
function SetCSIMTarget($aTarget) {
$this->csimtarget=$aTarget;
}
function SetCSIMAlt($aAlt) {
$this->csimalt=$aAlt;
}
function GetCSIMAreas(){
return $this->csimareas;
}
function AddCSIMPoly($aPts) {
$coords = round($aPts[0]).", ".round($aPts[1]);
$n = count($aPts)/2;
for( $i=1; $i < $n; ++$i){
$coords .= ", ".round($aPts[2*$i]).", ".round($aPts[2*$i+1]);
}
$this->csimareas="";
if( !empty($this->csimtarget) ) {
$this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtarget."\"";
if( !empty($this->csimalt) ) {
$tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
}
function AddCSIMCircle($x,$y,$r) {
$x = round($x); $y=round($y); $r=round($r);
$this->csimareas="";
if( !empty($this->csimtarget) ) {
$this->csimareas .= "<area shape=\"circle\" coords=\"$x,$y,$r\" href=\"".$this->csimtarget."\"";
if( !empty($this->csimalt) ) {
$tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
}
function Stroke($img,$x,$y) {
if( !$this->show ) return;
 
if( $this->iFormatCallback != '' || $this->iFormatCallback2 != '' ) {
 
if( $this->iFormatCallback != '' ) {
$f = $this->iFormatCallback;
list($width,$color,$fcolor) = $f($this->yvalue);
$filename = $this->iFileName;
$imgscale = $this->iScale;
}
else {
$f = $this->iFormatCallback2;
list($width,$color,$fcolor,$filename,$imgscale) = $f($this->yvalue,$this->xvalue);
if( $filename=="" ) $filename = $this->iFileName;
if( $imgscale=="" ) $imgscale = $this->iScale;
}
 
if( $width=="" ) $width = $this->width;
if( $color=="" ) $color = $this->color;
if( $fcolor=="" ) $fcolor = $this->fill_color;
 
}
else {
$fcolor = $this->fill_color;
$color = $this->color;
$width = $this->width;
$filename = $this->iFileName;
$imgscale = $this->iScale;
}
 
 
if( $this->type == MARK_IMG || $this->type >= MARK_IMG_PUSHPIN ) {
 
// Note: For the builtin images we use the "filename" parameter
// to denote the color
$anchor_x = 0.5;
$anchor_y = 0.5;
switch( $this->type ) {
case MARK_IMG :
// Load an image and use that as a marker
// Small optimization, if we have already read an image don't
// waste time reading it again.
if( $this->markimg == '' || !($this->oldfilename === $filename) ) {
$this->markimg = Graph::LoadBkgImage('',$filename);
$this->oldfilename = $filename ;
}
break;
 
case MARK_IMG_PUSHPIN:
case MARK_IMG_SPUSHPIN:
case MARK_IMG_LPUSHPIN:
if( $this->imgdata_pushpins == null ) {
require_once 'imgdata_pushpins.inc';
$this->imgdata_pushpins = new ImgData_PushPins();
}
$this->markimg = $this->imgdata_pushpins->GetImg($this->type,$filename);
list($anchor_x,$anchor_y) = $this->imgdata_pushpins->GetAnchor();
break;
 
case MARK_IMG_SQUARE:
if( $this->imgdata_squares == null ) {
require_once 'imgdata_squares.inc';
$this->imgdata_squares = new ImgData_Squares();
}
$this->markimg = $this->imgdata_squares->GetImg($this->type,$filename);
list($anchor_x,$anchor_y) = $this->imgdata_squares->GetAnchor();
break;
 
case MARK_IMG_STAR:
if( $this->imgdata_stars == null ) {
require_once 'imgdata_stars.inc';
$this->imgdata_stars = new ImgData_Stars();
}
$this->markimg = $this->imgdata_stars->GetImg($this->type,$filename);
list($anchor_x,$anchor_y) = $this->imgdata_stars->GetAnchor();
break;
 
case MARK_IMG_BEVEL:
if( $this->imgdata_bevels == null ) {
require_once 'imgdata_bevels.inc';
$this->imgdata_bevels = new ImgData_Bevels();
}
$this->markimg = $this->imgdata_bevels->GetImg($this->type,$filename);
list($anchor_x,$anchor_y) = $this->imgdata_bevels->GetAnchor();
break;
 
case MARK_IMG_DIAMOND:
if( $this->imgdata_diamonds == null ) {
require_once 'imgdata_diamonds.inc';
$this->imgdata_diamonds = new ImgData_Diamonds();
}
$this->markimg = $this->imgdata_diamonds->GetImg($this->type,$filename);
list($anchor_x,$anchor_y) = $this->imgdata_diamonds->GetAnchor();
break;
 
case MARK_IMG_BALL:
case MARK_IMG_SBALL:
case MARK_IMG_MBALL:
case MARK_IMG_LBALL:
if( $this->imgdata_balls == null ) {
require_once 'imgdata_balls.inc';
$this->imgdata_balls = new ImgData_Balls();
}
$this->markimg = $this->imgdata_balls->GetImg($this->type,$filename);
list($anchor_x,$anchor_y) = $this->imgdata_balls->GetAnchor();
break;
}
 
$w = imagesx($this->markimg);
$h = imagesy($this->markimg);
$dw = round($imgscale * $w );
$dh = round($imgscale * $h );
 
$dx = round($x-$dw*$anchor_x);
$dy = round($y-$dh*$anchor_y);
$this->width = max($dx,$dy);
$cp = $GLOBALS['copyfunc'] ;
$cp($img->img,$this->markimg,$dx,$dy,0,0,$dw,$dh,$w,$h);
if( !empty($this->csimtarget) ) {
$this->csimareas = "<area shape=\"rect\" coords=\"".
$dx.','.$dy.','.round($dx+$dw).','.round($dy+$dh).'" '.
"href=\"".$this->csimtarget."\"";
if( !empty($this->csimalt) ) {
$tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
// Stroke title
$this->title->Align("center","top");
$this->title->Stroke($img,$x,$y+round($dh/2));
return;
}
 
$weight = $this->weight;
$dx=round($width/2,0);
$dy=round($width/2,0);
$pts=0;
 
switch( $this->type ) {
case MARK_SQUARE:
$c[]=$x-$dx;$c[]=$y-$dy;
$c[]=$x+$dx;$c[]=$y-$dy;
$c[]=$x+$dx;$c[]=$y+$dy;
$c[]=$x-$dx;$c[]=$y+$dy;
$c[]=$x-$dx;$c[]=$y-$dy;
$pts=5;
break;
case MARK_UTRIANGLE:
++$dx;++$dy;
$c[]=$x-$dx;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
$c[]=$x;$c[]=$y-0.87*$dy;
$c[]=$x+$dx;$c[]=$y+0.87*$dy;
$c[]=$x-$dx;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
$pts=4;
break;
case MARK_DTRIANGLE:
++$dx;++$dy;
$c[]=$x;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
$c[]=$x-$dx;$c[]=$y-0.87*$dy;
$c[]=$x+$dx;$c[]=$y-0.87*$dy;
$c[]=$x;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
$pts=4;
break;
case MARK_DIAMOND:
$c[]=$x;$c[]=$y+$dy;
$c[]=$x-$dx;$c[]=$y;
$c[]=$x;$c[]=$y-$dy;
$c[]=$x+$dx;$c[]=$y;
$c[]=$x;$c[]=$y+$dy;
$pts=5;
break;
case MARK_LEFTTRIANGLE:
$c[]=$x;$c[]=$y;
$c[]=$x;$c[]=$y+2*$dy;
$c[]=$x+$dx*2;$c[]=$y;
$c[]=$x;$c[]=$y;
$pts=4;
break;
case MARK_RIGHTTRIANGLE:
$c[]=$x-$dx*2;$c[]=$y;
$c[]=$x;$c[]=$y+2*$dy;
$c[]=$x;$c[]=$y;
$c[]=$x-$dx*2;$c[]=$y;
$pts=4;
break;
case MARK_FLASH:
$dy *= 2;
$c[]=$x+$dx/2; $c[]=$y-$dy;
$c[]=$x-$dx+$dx/2; $c[]=$y+$dy*0.7-$dy;
$c[]=$x+$dx/2; $c[]=$y+$dy*1.3-$dy;
$c[]=$x-$dx+$dx/2; $c[]=$y+2*$dy-$dy;
$img->SetLineWeight($weight);
$img->SetColor($color);
$img->Polygon($c);
$img->SetLineWeight(1);
$this->AddCSIMPoly($c);
break;
}
 
if( $pts>0 ) {
$this->AddCSIMPoly($c);
$img->SetLineWeight($weight);
$img->SetColor($fcolor);
$img->FilledPolygon($c);
$img->SetColor($color);
$img->Polygon($c);
$img->SetLineWeight(1);
}
elseif( $this->type==MARK_CIRCLE ) {
$img->SetColor($color);
$img->Circle($x,$y,$width);
$this->AddCSIMCircle($x,$y,$width);
}
elseif( $this->type==MARK_FILLEDCIRCLE ) {
$img->SetColor($fcolor);
$img->FilledCircle($x,$y,$width);
$img->SetColor($color);
$img->Circle($x,$y,$width+1);
$this->AddCSIMCircle($x,$y,$width);
}
elseif( $this->type==MARK_CROSS ) {
// Oversize by a pixel to match the X
$img->SetColor($color);
$img->SetLineWeight($weight);
$img->Line($x,$y+$dy+1,$x,$y-$dy-1);
$img->Line($x-$dx-1,$y,$x+$dx+1,$y);
$this->AddCSIMCircle($x,$y,$dx);
}
elseif( $this->type==MARK_X ) {
$img->SetColor($color);
$img->SetLineWeight($weight);
$img->Line($x+$dx,$y+$dy,$x-$dx,$y-$dy);
$img->Line($x-$dx,$y+$dy,$x+$dx,$y-$dy);
$this->AddCSIMCircle($x,$y,$dx+$dy);
}
elseif( $this->type==MARK_STAR ) {
$img->SetColor($color);
$img->SetLineWeight($weight);
$img->Line($x+$dx,$y+$dy,$x-$dx,$y-$dy);
$img->Line($x-$dx,$y+$dy,$x+$dx,$y-$dy);
// Oversize by a pixel to match the X
$img->Line($x,$y+$dy+1,$x,$y-$dy-1);
$img->Line($x-$dx-1,$y,$x+$dx+1,$y);
$this->AddCSIMCircle($x,$y,$dx+$dy);
}
// Stroke title
$this->title->Align("center","center");
$this->title->Stroke($img,$x,$y);
}
} // Class
 
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_canvas.php
New file
0,0 → 1,59
<?php
/*=======================================================================
// File: JPGRAPH_CANVAS.PHP
// Description: Canvas drawing extension for JpGraph
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_canvas.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
//===================================================
// CLASS CanvasGraph
// Description: Creates a simple canvas graph which
// might be used together with the basic Image drawing
// primitives. Useful to auickoly produce some arbitrary
// graphic which benefits from all the functionality in the
// graph liek caching for example.
//===================================================
class CanvasGraph extends Graph {
//---------------
// CONSTRUCTOR
function CanvasGraph($aWidth=300,$aHeight=200,$aCachedName="",$timeout=0,$inline=1) {
$this->Graph($aWidth,$aHeight,$aCachedName,$timeout,$inline);
}
 
//---------------
// PUBLIC METHODS
 
function InitFrame() {
$this->StrokePlotArea();
}
 
// Method description
function Stroke($aStrokeFileName="") {
if( $this->texts != null ) {
for($i=0; $i<count($this->texts); ++$i) {
$this->texts[$i]->Stroke($this->img);
}
}
$this->StrokeTitles();
 
// If the filename is given as the special _IMG_HANDLER
// then the image handler is returned and the image is NOT
// streamed back
if( $aStrokeFileName == _IMG_HANDLER ) {
return $this->img->img;
}
else {
// Finally stream the generated picture
$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
$aStrokeFileName);
}
}
} // Class
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/jpgraph_pie.php
New file
0,0 → 1,855
<?php
/*=======================================================================
// File: JPGRAPH_PIE.PHP
// Description: Pie plot extension for JpGraph
// Created: 2001-02-14
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_pie.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
 
// Defines for PiePlot::SetLabelType()
DEFINE("PIE_VALUE_ABS",1);
DEFINE("PIE_VALUE_PER",0);
DEFINE("PIE_VALUE_PERCENTAGE",0);
 
//===================================================
// CLASS PiePlot
// Description: Draws a pie plot
//===================================================
class PiePlot {
var $posx=0.5,$posy=0.5;
var $radius=0.3;
var $explode_radius=array(),$explode_all=false,$explode_r=20;
var $labels=null, $legends=null;
var $csimtargets=null; // Array of targets for CSIM
var $csimareas=''; // Generated CSIM text
var $csimalts=null; // ALT tags for corresponding target
var $data=null;
var $title;
var $startangle=0;
var $weight=1, $color="black";
var $legend_margin=6,$show_labels=true;
var $themearr = array(
"earth" => array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
"pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
"water" => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
"sand" => array(27,168,34,170,19,50,65,72,131,209,46,393));
var $theme="earth";
var $setslicecolors=array();
var $labeltype=0; // Default to percentage
var $pie_border=true,$pie_interior_border=true;
var $value;
var $ishadowcolor='',$ishadowdrop=4;
var $ilabelposadj=1;
var $legendcsimtargets = array();
var $legendcsimalts = array();
//---------------
// CONSTRUCTOR
function PiePlot($data) {
$this->data = array_reverse($data);
$this->title = new Text("");
$this->title->SetFont(FF_FONT1,FS_BOLD);
$this->value = new DisplayValue();
$this->value->Show();
$this->value->SetFormat('%.1f%%');
}
 
//---------------
// PUBLIC METHODS
function SetCenter($x,$y=0.5) {
$this->posx = $x;
$this->posy = $y;
}
 
function SetColor($aColor) {
$this->color = $aColor;
}
function SetSliceColors($aColors) {
$this->setslicecolors = $aColors;
}
function SetShadow($aColor='darkgray',$aDropWidth=4) {
$this->ishadowcolor = $aColor;
$this->ishadowdrop = $aDropWidth;
}
 
function SetCSIMTargets($targets,$alts=null) {
$this->csimtargets=array_reverse($targets);
if( is_array($alts) )
$this->csimalts=array_reverse($alts);
}
function GetCSIMareas() {
return $this->csimareas;
}
 
function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
//Slice number, ellipse centre (x,y), height, width, start angle, end angle
while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
 
$sa = 2*M_PI - $sa;
$ea = 2*M_PI - $ea;
 
//add coordinates of the centre to the map
$coords = "$xc, $yc";
 
//add coordinates of the first point on the arc to the map
$xp = floor(($radius*cos($ea))+$xc);
$yp = floor($yc-$radius*sin($ea));
$coords.= ", $xp, $yp";
//add coordinates every 0.2 radians
$a=$ea+0.2;
while ($a<$sa) {
$xp = floor($radius*cos($a)+$xc);
$yp = floor($yc-$radius*sin($a));
$coords.= ", $xp, $yp";
$a += 0.2;
}
//Add the last point on the arc
$xp = floor($radius*cos($sa)+$xc);
$yp = floor($yc-$radius*sin($sa));
$coords.= ", $xp, $yp";
if( !empty($this->csimtargets[$i]) )
$this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
$this->csimtargets[$i]."\"";
if( !empty($this->csimalts[$i]) ) {
$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
 
function SetTheme($aTheme) {
if( in_array($aTheme,array_keys($this->themearr)) )
$this->theme = $aTheme;
else
JpGraphError::Raise("PiePLot::SetTheme() Unknown theme: $aTheme");
}
function ExplodeSlice($e,$radius=20) {
$this->explode_radius[$e]=$radius;
}
 
function ExplodeAll($radius=20) {
$this->explode_all=true;
$this->explode_r = $radius;
}
 
function Explode($aExplodeArr) {
if( !is_array($aExplodeArr) ) {
JpGraphError::Raise("Argument to PiePlot::Explode() must be an array.");
}
$this->explode_radius = $aExplodeArr;
}
 
function SetStartAngle($aStart) {
if( $aStart < 0 || $aStart > 360 ) {
JpGraphError::Raise('Slice start angle must be between 0 and 360 degrees.');
}
$this->startangle = 360-$aStart;
}
function SetFont($family,$style=FS_NORMAL,$size=10) {
JpGraphError::Raise('PiePlot::SetFont() is deprecated. Use PiePlot->value->SetFont() instead.');
}
// Size in percentage
function SetSize($aSize) {
if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
$this->radius = $aSize;
else
JpGraphError::Raise("PiePlot::SetSize() Radius for pie must either be specified as a fraction
[0, 0.5] of the size of the image or as an absolute size in pixels
in the range [10, 1000]");
}
function SetFontColor($aColor) {
JpGraphError::Raise('PiePlot::SetFontColor() is deprecated. Use PiePlot->value->SetColor() instead.');
}
// Set label arrays
function SetLegends($aLegend) {
$this->legends = $aLegend;
}
 
// Set text labels for slices
function SetLabels($aLabels,$aLblPosAdj="auto") {
$this->labels = array_reverse($aLabels);
$this->ilabelposadj=$aLblPosAdj;
}
 
function SetLabelPos($aLblPosAdj) {
$this->ilabelposadj=$aLblPosAdj;
}
// Should we display actual value or percentage?
function SetLabelType($t) {
if( $t<0 || $t>1 )
JpGraphError::Raise("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
$this->labeltype=$t;
}
 
function SetValueType($aType) {
$this->SetLabelType($aType);
}
 
 
// Should the circle around a pie plot be displayed
function ShowBorder($exterior=true,$interior=true) {
$this->pie_border = $exterior;
$this->pie_interior_border = $interior;
}
// Setup the legends
function Legend(&$graph) {
$colors = array_keys($graph->img->rgb->rgb_table);
sort($colors);
$ta=$this->themearr[$this->theme];
$n = count($this->data);
 
if( $this->setslicecolors==null )
$numcolors=count($ta);
else {
$this->setslicecolors = array_slice($this->setslicecolors,0,$n);
$numcolors=$n;
if( $graph->pieaa && get_class($this)==='pieplot' ) {
$this->setslicecolors = array_reverse($this->setslicecolors);
}
}
$sum=0;
for($i=0; $i < $n; ++$i)
$sum += $this->data[$i];
 
// Bail out with error if the sum is 0
if( $sum==0 )
JpGraphError::Raise("Illegal pie plot. Sum of all data is zero for Pie!");
 
// Make sure we don't plot more values than data points
// (in case the user added more legends than data points)
$n = min(count($this->legends),count($this->data));
if( $this->legends != "" )
$this->legends = array_reverse($this->legends);
for( $i=$n-1; $i >= 0; --$i ) {
$l = $this->legends[$i];
// Replace possible format with actual values
if( $this->labeltype==0 ) {
$l = sprintf($l,100*$this->data[$i]/$sum);
}
else {
$l = sprintf($l,$this->data[$i]);
}
$alt = sprintf($this->csimalts[$i],$this->data[$i]);
if( $this->setslicecolors==null )
$graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,
$this->csimtargets[$i],$alt);
else
$graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,
$this->csimtargets[$i],$alt);
}
}
function Stroke(&$img,$aaoption=0) {
// aaoption is used to handle antialias
// aaoption == 0 a normal pie
// aaoption == 1 just the body
// aaoption == 2 just the values
 
// Explode scaling. If anti anti alias we scale the image
// twice and we also need to scale the exploding distance
$expscale = $aaoption === 1 ? 2 : 1;
 
$colors = array_keys($img->rgb->rgb_table);
sort($colors);
$ta=$this->themearr[$this->theme];
$n = count($this->data);
if( $this->setslicecolors==null )
$numcolors=count($ta);
else {
$this->setslicecolors = array_reverse(array_slice($this->setslicecolors,0,$n));
$numcolors=$n;
}
// Draw the slices
$sum=0;
for($i=0; $i < $n; ++$i)
$sum += $this->data[$i];
// Bail out with error if the sum is 0
if( $sum==0 )
JpGraphError::Raise("Sum of all data is 0 for Pie.");
// Set up the pie-circle
if( $this->radius <= 1 )
$radius = floor($this->radius*min($img->width,$img->height));
else {
$radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
}
 
if( $this->posx <= 1 && $this->posx > 0 )
$xc = round($this->posx*$img->width);
else
$xc = $this->posx ;
if( $this->posy <= 1 && $this->posy > 0 )
$yc = round($this->posy*$img->height);
else
$yc = $this->posy ;
$this->startangle = $this->startangle*M_PI/180;
 
$n = count($this->data);
 
if( $this->explode_all )
for($i=0; $i < $n; ++$i)
$this->explode_radius[$i]=$this->explode_r;
 
 
if( $this->ishadowcolor != "" && $aaoption !== 2) {
$accsum=0;
$angle2 = $this->startangle;
$img->SetColor($this->ishadowcolor);
for($i=0; $sum>0 && $i < $n; ++$i) {
$d = $this->data[$i];
$angle1 = $angle2;
$accsum += $d;
$angle2 = $this->startangle+2*M_PI*$accsum/$sum;
if( empty($this->explode_radius[$i]) )
$this->explode_radius[$i]=0;
 
$la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
$xcm = $xc + $this->explode_radius[$i]*cos($la)*$expscale;
$ycm = $yc - $this->explode_radius[$i]*sin($la)*$expscale;
$xcm += $this->ishadowdrop*$expscale;
$ycm += $this->ishadowdrop*$expscale;
 
$img->CakeSlice($xcm,$ycm,$radius,$radius,
$angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor);
}
}
 
$accsum=0;
$angle2 = $this->startangle;
$img->SetColor($this->color);
for($i=0; $sum>0 && $i < $n; ++$i) {
$d = $this->data[$i];
$angle1 = $angle2;
$accsum += $d;
$angle2 = $this->startangle+2*M_PI*$accsum/$sum;
 
if( $this->setslicecolors==null )
$slicecolor=$colors[$ta[$i%$numcolors]];
else
$slicecolor=$this->setslicecolors[$i%$numcolors];
 
if( $this->pie_interior_border && $aaoption===0 )
$img->SetColor($this->color);
else
$img->SetColor($slicecolor);
 
$arccolor = $this->pie_border && $aaoption===0 ? $this->color : "";
 
$this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
 
if( empty($this->explode_radius[$i]) )
$this->explode_radius[$i]=0;
 
$xcm = $xc + $this->explode_radius[$i]*cos($this->la[$i])*$expscale;
$ycm = $yc - $this->explode_radius[$i]*sin($this->la[$i])*$expscale;
 
if( $aaoption !== 2 ) {
$img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,
$angle1*180/M_PI,$angle2*180/M_PI,$slicecolor,$arccolor);
}
 
if ($this->csimtargets && $aaoption !== 1 )
$this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2);
 
}
 
if( $this->value->show && $aaoption !== 1 ) {
// Format the titles for each slice
for( $i=0; $i < $n; ++$i) {
if( $this->labeltype==0 )
if( $sum != 0 )
$l = 100.0*$this->data[$i]/$sum;
else
$l = 0.0;
else
$l = $this->data[$i]*1.0;
if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
$this->labels[$i]=sprintf($this->labels[$i],$l);
else
$this->labels[$i]=$l;
}
 
$this->StrokeAllLabels($img,$xc,$yc,$radius);
}
 
// Adjust title position
if( $aaoption !== 1 ) {
$this->title->Pos($xc,
$yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin,
"center","bottom");
$this->title->Stroke($img);
}
 
}
 
//---------------
// PRIVATE METHODS
 
function StrokeAllLabels($img,$xc,$yc,$radius) {
$n = count($this->labels);
for($i=0; $i < $n; ++$i) {
$this->StrokeLabel($this->labels[$i],$img,$xc,$yc,$this->la[$i],
$radius + $this->explode_radius[$i]);
}
}
 
// Position the labels of each slice
function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
 
// Default value
if( $this->ilabelposadj === 'auto' )
$this->ilabelposadj = 0.65;
 
// We position the values diferently depending on if they are inside
// or outside the pie
if( $this->ilabelposadj < 1.0 ) {
 
$this->value->SetAlign('center','center');
$this->value->margin = 0;
$xt=round($this->ilabelposadj*$r*cos($a)+$xc);
$yt=round($yc-$this->ilabelposadj*$r*sin($a));
$this->value->Stroke($img,$label,$xt,$yt);
}
else {
 
$this->value->halign = "left";
$this->value->valign = "top";
$this->value->margin = 0;
// Position the axis title.
// dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
// that intersects with the extension of the corresponding axis. The code looks a little
// bit messy but this is really the only way of having a reasonable position of the
// axis titles.
$img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
$h=$img->GetTextHeight($label);
// For numeric values the format of the display value
// must be taken into account
if( is_numeric($label) ) {
if( $label > 0 )
$w=$img->GetTextWidth(sprintf($this->value->format,$label));
else
$w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
}
else
$w=$img->GetTextWidth($label);
 
if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
$r *= $this->ilabelposadj;
}
 
$r += $img->GetFontHeight()/1.5;
$xt=round($r*cos($a)+$xc);
$yt=round($yc-$r*sin($a));
 
while( $a > 2*M_PI ) $a -= 2*M_PI;
if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
 
$this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
}
}
} // Class
 
 
//===================================================
// CLASS PiePlotC
// Description: Same as a normal pie plot but with a
// filled circle in the center
//===================================================
class PiePlotC extends PiePlot {
var $imidsize=0.5; // Fraction of total width
var $imidcolor='white';
var $midtitle='';
var $middlecsimtarget="",$middlecsimalt="";
 
function PiePlotC($data,$aCenterTitle='') {
parent::PiePlot($data);
$this->midtitle = new Text();
$this->midtitle->ParagraphAlign('center');
}
 
function SetMid($aTitle,$aColor='white',$aSize=0.5) {
$this->midtitle->Set($aTitle);
$this->imidsize = $aSize ;
$this->imidcolor = $aColor ;
}
 
function SetMidTitle($aTitle) {
$this->midtitle->Set($aTitle);
}
 
function SetMidSize($aSize) {
$this->imidsize = $aSize ;
}
 
function SetMidColor($aColor) {
$this->imidcolor = $aColor ;
}
 
function SetMidCSIM($aTarget,$aAlt) {
$this->middlecsimtarget = $aTarget;
$this->middlecsimalt = $aAlt;
}
 
function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
//Slice number, ellipse centre (x,y), radius, start angle, end angle
while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
 
$sa = 2*M_PI - $sa;
$ea = 2*M_PI - $ea;
 
// Add inner circle first point
$xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
$yp = floor($yc-($this->imidsize*$radius*sin($ea)));
$coords = "$xp, $yp";
//add coordinates every 0.25 radians
$a=$ea+0.25;
while ($a < $sa) {
$xp = floor(($this->imidsize*$radius*cos($a)+$xc));
$yp = floor($yc-($this->imidsize*$radius*sin($a)));
$coords.= ", $xp, $yp";
$a += 0.25;
}
 
// Make sure we end at the last point
$xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
$yp = floor($yc-($this->imidsize*$radius*sin($sa)));
$coords.= ", $xp, $yp";
 
// Straight line to outer circle
$xp = floor($radius*cos($sa)+$xc);
$yp = floor($yc-$radius*sin($sa));
$coords.= ", $xp, $yp";
 
//add coordinates every 0.25 radians
$a=$sa - 0.25;
while ($a > $ea) {
$xp = floor($radius*cos($a)+$xc);
$yp = floor($yc-$radius*sin($a));
$coords.= ", $xp, $yp";
$a -= 0.25;
}
//Add the last point on the arc
$xp = floor($radius*cos($ea)+$xc);
$yp = floor($yc-$radius*sin($ea));
$coords.= ", $xp, $yp";
 
// Close the arc
$xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
$yp = floor($yc-($this->imidsize*$radius*sin($ea)));
$coords .= ", $xp, $yp";
 
if( !empty($this->csimtargets[$i]) )
$this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
$this->csimtargets[$i]."\"";
if( !empty($this->csimalts[$i]) ) {
$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
 
 
function Stroke($img,$aaoption=0) {
 
// Stroke the pie but don't stroke values
$tmp = $this->value->show;
$this->value->show = false;
parent::Stroke($img,$aaoption);
$this->value->show = $tmp;
 
$xc = round($this->posx*$img->width);
$yc = round($this->posy*$img->height);
 
$radius = floor($this->radius * min($img->width,$img->height)) ;
 
 
if( $this->imidsize > 0 && $aaoption !== 2 ) {
 
if( $this->ishadowcolor != "" ) {
$img->SetColor($this->ishadowcolor);
$img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
round($radius*$this->imidsize));
}
 
$img->SetColor($this->imidcolor);
$img->FilledCircle($xc,$yc,round($radius*$this->imidsize));
 
if( $this->pie_border && $aaoption === 0 ) {
$img->SetColor($this->color);
$img->Circle($xc,$yc,round($radius*$this->imidsize));
}
 
if( !empty($this->middlecsimtarget) )
$this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));
 
}
 
if( $this->value->show && $aaoption !== 1) {
$this->StrokeAllLabels($img,$xc,$yc,$radius);
$this->midtitle->Pos($xc,$yc,'center','center');
$this->midtitle->Stroke($img);
}
 
}
 
function AddMiddleCSIM($xc,$yc,$r) {
$this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
$this->middlecsimtarget."\"";
if( !empty($this->middlecsimalt) ) {
$tmp = $this->middlecsimalt;
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
 
function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
 
if( $this->ilabelposadj === 'auto' )
$this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;
 
parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);
 
}
 
}
 
 
//===================================================
// CLASS PieGraph
// Description:
//===================================================
class PieGraph extends Graph {
var $posx, $posy, $radius;
var $legends=array();
var $plots=array();
var $pieaa = false ;
//---------------
// CONSTRUCTOR
function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
$this->Graph($width,$height,$cachedName,$timeout,$inline);
$this->posx=$width/2;
$this->posy=$height/2;
$this->SetColor(array(255,255,255));
}
 
//---------------
// PUBLIC METHODS
function Add($aObj) {
 
if( is_array($aObj) && count($aObj) > 0 )
$cl = get_class($aObj[0]);
else
$cl = get_class($aObj);
 
if( $cl == 'text' )
$this->AddText($aObj);
else
$this->plots[] = $aObj;
}
 
function SetAntiAliasing($aFlg=true) {
$this->pieaa = $aFlg;
}
function SetColor($c) {
$this->SetMarginColor($c);
}
 
 
function DisplayCSIMAreas() {
$csim="";
foreach($this->plots as $p ) {
$csim .= $p->GetCSIMareas();
}
//$csim.= $this->legend->GetCSIMareas();
if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
$this->img->SetColor($this->csimcolor);
for ($i=0; $i<count($coords[0]); $i++) {
if ($coords[1][$i]=="poly") {
preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
$this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
for ($j=0; $j<count($pts[0]); $j++) {
$this->img->LineTo($pts[1][$j],$pts[2][$j]);
}
} else if ($coords[1][$i]=="rect") {
$pts = preg_split('/,/', $coords[2][$i]);
$this->img->SetStartPoint($pts[0],$pts[1]);
$this->img->LineTo($pts[2],$pts[1]);
$this->img->LineTo($pts[2],$pts[3]);
$this->img->LineTo($pts[0],$pts[3]);
$this->img->LineTo($pts[0],$pts[1]);
}
}
}
}
 
// Method description
function Stroke($aStrokeFileName="") {
// If the filename is the predefined value = '_csim_special_'
// we assume that the call to stroke only needs to do enough
// to correctly generate the CSIM maps.
// We use this variable to skip things we don't strictly need
// to do to generate the image map to improve performance
// a best we can. Therefor you will see a lot of tests !$_csim in the
// code below.
$_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
 
// We need to know if we have stroked the plot in the
// GetCSIMareas. Otherwise the CSIM hasn't been generated
// and in the case of GetCSIM called before stroke to generate
// CSIM without storing an image to disk GetCSIM must call Stroke.
$this->iHasStroked = true;
 
$n = count($this->plots);
 
if( $this->pieaa ) {
 
if( !$_csim ) {
if( $this->background_image != "" ) {
$this->StrokeFrameBackground();
}
else {
$this->StrokeFrame();
}
}
 
 
$w = $this->img->width;
$h = $this->img->height;
$oldimg = $this->img->img;
 
$this->img->CreateImgCanvas(2*$w,2*$h);
$this->img->SetColor( $this->margin_color );
$this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);
 
for($i=0; $i < $n; ++$i) {
if( $this->plots[$i]->posx > 1 )
$this->plots[$i]->posx *= 2 ;
if( $this->plots[$i]->posy > 1 )
$this->plots[$i]->posy *= 2 ;
 
$this->plots[$i]->Stroke($this->img,1);
 
if( $this->plots[$i]->posx > 1 )
$this->plots[$i]->posx /= 2 ;
if( $this->plots[$i]->posy > 1 )
$this->plots[$i]->posy /= 2 ;
}
 
$indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
$indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
$this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
$w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));
 
$this->img->img = $oldimg ;
$this->img->width = $w ;
$this->img->height = $h ;
 
for($i=0; $i < $n; ++$i) {
$this->plots[$i]->Stroke($this->img,2);
$this->plots[$i]->Legend($this);
}
 
}
else {
 
if( !$_csim ) {
if( $this->background_image != "" ) {
$this->StrokeFrameBackground();
}
else {
$this->StrokeFrame();
}
}
for($i=0; $i < $n; ++$i) {
$this->plots[$i]->Stroke($this->img);
$this->plots[$i]->Legend($this);
}
}
 
 
$this->legend->Stroke($this->img);
$this->footer->Stroke($this->img);
 
if( !$_csim ) {
$this->StrokeTitles();
 
// Stroke texts
if( $this->texts != null ) {
$n = count($this->texts);
for($i=0; $i < $n; ++$i ) {
$this->texts[$i]->Stroke($this->img);
}
}
 
if( JPG_DEBUG ) {
$this->DisplayCSIMAreas();
}
// If the filename is given as the special "__handle"
// then the image handler is returned and the image is NOT
// streamed back
if( $aStrokeFileName == _IMG_HANDLER ) {
return $this->img->img;
}
else {
// Finally stream the generated picture
$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
$aStrokeFileName);
}
}
}
} // Class
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/imgdata_bevels.inc
New file
0,0 → 1,106
<?php
//=======================================================================
// File: IMGDATA_BEVELS.INC
// Description: Base64 encoded images for round bevels
// Created: 2003-03-20
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: imgdata_bevels.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
class ImgData_Bevels extends ImgData {
var $name = 'Round Bevels';
var $an = array(MARK_IMG_BEVEL => 'imgdata');
var $colors = array('green','purple','orange','red','yellow');
var $index = array('green'=>1,'purple'=>4,'orange'=>2,'red'=>0,'yellow'=>3);
var $maxidx = 4 ;
 
var $imgdata ;
 
function ImgData_Bevels() {
//==========================================================
// File: bullets_balls_red_013.png
//==========================================================
$this->imgdata[0][0]= 337 ;
$this->imgdata[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
'BMVEX////////27t/f3+LFwcmNxMuxm62DmqKth1VpZmIWg6fv'.
'HCa7K0BwMEytCjFnIyUlEBg9vhQvAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
'RQfTAxcBNhk+pYJVAAAAl0lEQVR4nE2Q2xLDIAgFHUWBKJf//9'.
'oekmbafVDZARRbK/pYTKP9WNcNv64zzUdd9BjmrgnsVXRNSzO3'.
'CJ5ahdhy0XKQkxld1kxb45j7dp0x2lBNOyVgQpMaoadX7Hs7zr'.
'P1yKj47DKBnKaBKiSAkNss7O6PkMx6kIgYXISQJpcZCqdY6KR+'.
'J1PkS5Xob/h7MNz8x6D3fz5DKQjpkZOBYAAAAABJRU5ErkJggg'.
'==' ;
 
//==========================================================
// File: bullets_balls_green_013.png
//==========================================================
$this->imgdata[1][0]= 344 ;
$this->imgdata[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
'BMVEX////////27t/e3+K3vriUub/Dm18j4xc3ob10k0ItqQlU'.
'e5JBmwpxY1ENaKBgUh0iHgwsSre9AAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
'RQfTAxcBNTfJXtxZAAAAnklEQVR4nE2QWY4EMQhDUVhSIRC4/2'.
'kbaqLp9p+f2AxAayAzDfiK9znPORuvH0x8Ss9z6I9sHp6tcxE9'.
'nLmWmebmt5F5p2AR0+C9AWpLBjXRaZsCAT3SqklVp0YkAWaGtd'.
'c5Z41/STYpPzW7BjyiRrwkVmQto/Cw9tNEMvsgcekyCyFPboIu'.
'IsuXiKffYB4NK4r/h6d4g9HPPwCR7i8+GscIiiaonUAAAAAASU'.
'VORK5CYII=' ;
 
//==========================================================
// File: bullets_balls_oy_035.png
//==========================================================
$this->imgdata[2][0]= 341 ;
$this->imgdata[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
'BMVEX////////27t/f3+K5tbqNwcjnkjXjbxR2i5anfEoNkbis'.
'PBxpU0sZbZejKgdqIRIlERIwYtkYAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
'RQfTAxcBNgK0wEu5AAAAm0lEQVR4nE3QVxIEIQgEUErAgTHA/U'.
'+7zbipf9RXgoGo0liMmX6RdSPLPtZM9F4LuuSIaZtZWffiU6Iz'.
'Y8SOMF0NogBj30ioGRGLZgiPvce1TbIRz6oBQEbOFGK0rIoxrn'.
'5hDomMA1cfGRCaRVhjS3gkzheM+4HtnlkXcvdZhWG4qZawewe6'.
'9Jnz/TKLB/ML6HUepn//QczazuwFO/0Ivpolhi4AAAAASUVORK'.
'5CYII=' ;
 
//==========================================================
// File: bullets_balls_oy_036.png
//==========================================================
$this->imgdata[3][0]= 340 ;
$this->imgdata[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
'BMVEX////////27t/e3+LO3hfYzz65ubiNwci6uQ12ipadgVGa'.
'fwsNkbhnVkcaZ5dwSA8lFg7CEepmAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElN'.
'RQfTAxcCBySi1nevAAAAjElEQVR4nFXPWw7EIAgFUNMoCMhj/6'.
'staKczc/2RkwjS2glQ+w3YytgXCXCZpRo8gJdGxZadJws13CUP'.
'4SZI4MYiUxypeiGGw1XShVBTNN9kLXP2GRrZPFvKgd7z/sqGGV'.
'7C7r7r3l09alYN3iA8Yn+ImdVrNoEeSRqJPAaHfhZzLYwXstdZ'.
'rP3n2bvdAI4INwtihiwAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bullets_balls_pp_019.png
//==========================================================
$this->imgdata[4][0]= 334 ;
$this->imgdata[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
'BMVEX////+/v7i4eO/w8eHxcvKroNVormtfkjrMN2BeXQrepPc'.
'Esy4IL+OFaR7F25LHF8mFRh5XXtUAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
'RQfTAxcBNgkjEpIxAAAAlElEQVR4nE2QAQ7FIAhDDTAVndL7n3'.
'ZV/7JfEwMvFIWUlkTMVNInbVv5ZeJqG7Smh2QTBwJBpsdizAZP'.
'5NyW0awhK8kYodnZxS6ECvPRp2sI+y7PBv1mN02KH7h77QCJ8D'.
'4VvY5NUgEmCwj6ZMzHtJRgRSXwC1gfcqJJH0GBnSnK1kUQ72DY'.
'CPBv+MCS/e0jib77eQAJxwiEWm7hFwAAAABJRU5ErkJggg==' ;
 
}
}
 
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_gantt.php
New file
0,0 → 1,2111
<?php
/*=======================================================================
// File: JPGRAPH_GANTT.PHP
// Description: JpGraph Gantt plot extension
// Created: 2001-11-12
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_gantt.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (c) 2002 Johan Persson
//========================================================================
*/
// Scale Header types
DEFINE("GANTT_HDAY",1);
DEFINE("GANTT_HWEEK",2);
DEFINE("GANTT_HMONTH",4);
DEFINE("GANTT_HYEAR",8);
 
// Bar patterns
DEFINE("GANTT_RDIAG",BAND_RDIAG); // Right diagonal lines
DEFINE("GANTT_LDIAG",BAND_LDIAG); // Left diagonal lines
DEFINE("GANTT_SOLID",BAND_SOLID); // Solid one color
DEFINE("GANTT_VLINE",BAND_VLINE); // Vertical lines
DEFINE("GANTT_HLINE",BAND_HLINE); // Horizontal lines
DEFINE("GANTT_3DPLANE",BAND_3DPLANE); // "3D" Plane
DEFINE("GANTT_HVCROSS",BAND_HVCROSS); // Vertical/Hor crosses
DEFINE("GANTT_DIAGCROSS",BAND_DIAGCROSS); // Diagonal crosses
 
// Conversion constant
DEFINE("SECPERDAY",3600*24);
 
// Locales. ONLY KEPT FOR BACKWARDS COMPATIBILITY
// You should use the proper locale strings directly
// from now on.
DEFINE("LOCALE_EN","en_UK");
DEFINE("LOCALE_SV","sv_SE");
 
// Layout of bars
DEFINE("GANTT_EVEN",1);
DEFINE("GANTT_FROMTOP",2);
 
// Styles for week header
DEFINE("WEEKSTYLE_WNBR",0);
DEFINE("WEEKSTYLE_FIRSTDAY",1);
DEFINE("WEEKSTYLE_FIRSTDAY2",2);
DEFINE("WEEKSTYLE_FIRSTDAYWNBR",3);
DEFINE("WEEKSTYLE_FIRSTDAY2WNBR",4);
 
// Styles for month header
DEFINE("MONTHSTYLE_SHORTNAME",0);
DEFINE("MONTHSTYLE_LONGNAME",1);
DEFINE("MONTHSTYLE_LONGNAMEYEAR2",2);
DEFINE("MONTHSTYLE_SHORTNAMEYEAR2",3);
DEFINE("MONTHSTYLE_LONGNAMEYEAR4",4);
DEFINE("MONTHSTYLE_SHORTNAMEYEAR4",5);
 
 
// Types of constrain links
DEFINE('CONSTRAIN_STARTSTART',0);
DEFINE('CONSTRAIN_STARTEND',1);
DEFINE('CONSTRAIN_ENDSTART',2);
DEFINE('CONSTRAIN_ENDEND',3);
 
// Arrow direction for constrain links
DEFINE('ARROW_DOWN',0);
DEFINE('ARROW_UP',1);
DEFINE('ARROW_LEFT',2);
DEFINE('ARROW_RIGHT',3);
 
// Arrow type for constrain type
DEFINE('ARROWT_SOLID',0);
DEFINE('ARROWT_OPEN',1);
 
// Arrow size for constrain lines
DEFINE('ARROW_S1',0);
DEFINE('ARROW_S2',1);
DEFINE('ARROW_S3',2);
DEFINE('ARROW_S4',3);
DEFINE('ARROW_S5',4);
 
// Activity types for use with utility method CreateSimple()
DEFINE('ACTYPE_NORMAL',0);
DEFINE('ACTYPE_GROUP',1);
DEFINE('ACTYPE_MILESTONE',2);
 
 
//===================================================
// CLASS GanttGraph
// Description: Main class to handle gantt graphs
//===================================================
class GanttGraph extends Graph {
var $scale; // Public accessible
var $iObj=array(); // Gantt objects
var $iLabelHMarginFactor=0.2; // 10% margin on each side of the labels
var $iLabelVMarginFactor=0.4; // 40% margin on top and bottom of label
var $iLayout=GANTT_FROMTOP; // Could also be GANTT_EVEN
var $iSimpleFont = FF_FONT1,$iSimpleFontSize=11;
var $iSimpleStyle=GANTT_RDIAG,$iSimpleColor='yellow',$iSimpleBkgColor='red';
var $iSimpleProgressBkgColor='gray',$iSimpleProgressColor='darkgreen';
var $iSimpleProgressStyle=GANTT_SOLID;
//---------------
// CONSTRUCTOR
// Create a new gantt graph
function GanttGraph($aWidth=0,$aHeight=0,$aCachedName="",$aTimeOut=0,$aInline=true) {
Graph::Graph($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline);
$this->scale = new GanttScale($this->img);
if( $aWidth > 0 )
$this->img->SetMargin($aWidth/17,$aWidth/17,$aHeight/7,$aHeight/10);
$this->scale->ShowHeaders(GANTT_HWEEK|GANTT_HDAY);
$this->SetBox();
}
//---------------
// PUBLIC METHODS
 
//
 
function SetSimpleFont($aFont,$aSize) {
$this->iSimpleFont = $aFont;
$this->iSimpleFontSize = $aSize;
}
 
function SetSimpleStyle($aBand,$aColor,$aBkgColor) {
$this->iSimpleStyle = $aBand;
$this->iSimpleColor = $aColor;
$this->iSimpleBkgColor = $aSimpleBkgColor;
}
 
// A utility function to help create the Gantt charts
function CreateSimple($data,$constrains=array(),$progress=array()) {
for( $i=0; $i < count($data); ++$i) {
switch( $data[$i][1] ) {
case ACTYPE_GROUP:
// Create a slightly smaller height bar since the
// "wings" at the end will make it look taller
$a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',8);
$a->title->SetFont($this->iSimpleFont,FS_BOLD,$this->iSimpleFontSize);
$a->rightMark->Show();
$a->rightMark->SetType(MARK_RIGHTTRIANGLE);
$a->rightMark->SetWidth(8);
$a->rightMark->SetColor('black');
$a->rightMark->SetFillColor('black');
$a->leftMark->Show();
$a->leftMark->SetType(MARK_LEFTTRIANGLE);
$a->leftMark->SetWidth(8);
$a->leftMark->SetColor('black');
$a->leftMark->SetFillColor('black');
$a->SetPattern(BAND_SOLID,'black');
$csimpos = 6;
break;
case ACTYPE_NORMAL:
$a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',10);
$a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
$a->SetPattern($this->iSimpleStyle,$this->iSimpleColor);
$a->SetFillColor($this->iSimpleBkgColor);
// Check if this activity should have a constrain line
$n = count($constrains);
for( $j=0; $j < $n; ++$j ) {
if( $constrains[$j][0]==$data[$i][0] ) {
$a->SetConstrain($constrains[$j][1],$constrains[$j][2],'black',ARROW_S2,ARROWT_SOLID);
break;
}
}
 
// Check if this activity have a progress bar
$n = count($progress);
for( $j=0; $j < $n; ++$j ) {
if( $progress[$j][0]==$data[$i][0] ) {
$a->progress->Set($progress[$j][1]);
$a->progress->SetPattern($this->iSimpleProgressStyle,
$this->iSimpleProgressColor);
$a->progress->SetFillColor($this->iSimpleProgressBkgColor);
//$a->progress->SetPattern($progress[$j][2],$progress[$j][3]);
break;
}
}
$csimpos = 6;
break;
 
case ACTYPE_MILESTONE:
$a = new MileStone($data[$i][0],$data[$i][2],$data[$i][3]);
$a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
$csimpos = 5;
break;
default:
die('Unknown activity type');
break;
}
 
// Setup caption
$a->caption->Set($data[$i][$csimpos-1]);
 
// Check if this activity should have a CSIM target ?
if( !empty($data[$i][$csimpos]) ) {
$a->SetCSIMTarget($data[$i][$csimpos]);
$a->SetCSIMAlt($data[$i][$csimpos+1]);
}
if( !empty($data[$i][$csimpos+2]) ) {
$a->title->SetCSIMTarget($data[$i][$csimpos+2]);
$a->title->SetCSIMAlt($data[$i][$csimpos+3]);
}
 
$this->Add($a);
}
}
 
// Set what headers should be shown
function ShowHeaders($aFlg) {
$this->scale->ShowHeaders($aFlg);
}
// Specify the fraction of the font height that should be added
// as vertical margin
function SetLabelVMarginFactor($aVal) {
$this->iLabelVMarginFactor = $aVal;
}
// Add a new Gantt object
function Add($aObject) {
if( is_array($aObject) ) {
for($i=0; $i<count($aObject); ++$i)
$this->iObj[] = $aObject[$i];
}
else
$this->iObj[] = $aObject;
}
 
// Override inherit method from Graph and give a warning message
function SetScale() {
JpGraphError::Raise("SetScale() is not meaningfull with Gantt charts.");
// Empty
}
 
// Specify the date range for Gantt graphs (if this is not set it will be
// automtically determined from the input data)
function SetDateRange($aStart,$aEnd) {
$this->scale->SetRange($aStart,$aEnd);
}
// Get the maximum width of the titles for the bars
function GetMaxLabelWidth() {
$m=0;
if( $this->iObj != null ) {
$m = $this->iObj[0]->title->GetWidth($this->img);
for($i=1; $i<count($this->iObj); ++$i) {
if( $this->iObj[$i]->title->HasTabs() ) {
list($tot,$w) = $this->iObj[$i]->title->GetWidth($this->img,true);
$m=max($m,$tot);
}
else
$m=max($m,$this->iObj[$i]->title->GetWidth($this->img));
}
}
return $m;
}
// Get the maximum height of the titles for the bars
function GetMaxLabelHeight() {
$m=0;
if( $this->iObj != null ) {
$m = $this->iObj[0]->title->GetHeight($this->img);
for($i=1; $i<count($this->iObj); ++$i) {
$m=max($m,$this->iObj[$i]->title->GetHeight($this->img));
}
}
return $m;
}
 
function GetMaxBarAbsHeight() {
$m=0;
if( $this->iObj != null ) {
$m = $this->iObj[0]->GetAbsHeight($this->img);
for($i=1; $i<count($this->iObj); ++$i) {
$m=max($m,$this->iObj[$i]->GetAbsHeight($this->img));
}
}
return $m;
}
// Get the maximum used line number (vertical position) for bars
function GetBarMaxLineNumber() {
$m=0;
if( $this->iObj != null ) {
$m = $this->iObj[0]->GetLineNbr();
for($i=1; $i<count($this->iObj); ++$i) {
$m=max($m,$this->iObj[$i]->GetLineNbr());
}
}
return $m;
}
// Get the minumum and maximum used dates for all bars
function GetBarMinMax() {
$max=$this->scale->NormalizeDate($this->iObj[0]->GetMaxDate());
$min=$this->scale->NormalizeDate($this->iObj[0]->GetMinDate());
for($i=1; $i<count($this->iObj); ++$i) {
$max=Max($max,$this->scale->NormalizeDate($this->iObj[$i]->GetMaxDate()));
$min=Min($min,$this->scale->NormalizeDate($this->iObj[$i]->GetMinDate()));
}
$minDate = date("Y-m-d",$min);
$min = strtotime($minDate);
$maxDate = date("Y-m-d",$max);
$max = strtotime($maxDate);
return array($min,$max);
}
 
// Stroke the gantt chart
function Stroke($aStrokeFileName="") {
 
// If the filename is the predefined value = '_csim_special_'
// we assume that the call to stroke only needs to do enough
// to correctly generate the CSIM maps.
// We use this variable to skip things we don't strictly need
// to do to generate the image map to improve performance
// a best we can. Therefor you will see a lot of tests !$_csim in the
// code below.
$_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
 
// Should we autoscale dates?
if( !$this->scale->IsRangeSet() ) {
list($min,$max) = $this->GetBarMinMax();
$this->scale->SetRange($min,$max);
}
 
$this->scale->AdjustStartEndDay();
if( $this->img->img == null ) {
// The predefined left, right, top, bottom margins.
// Note that the top margin might incease depending on
// the title.
$lm=30;$rm=30;$tm=20;$bm=30;
if( BRAND_TIMING ) $bm += 10;
// First find out the height
$n=$this->GetBarMaxLineNumber()+1;
$m=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
$height=$n*((1+$this->iLabelVMarginFactor)*$m);
// Add the height of the scale titles
$h=$this->scale->GetHeaderHeight();
$height += $h;
 
// Calculate the top margin needed for title and subtitle
if( $this->title->t != "" ) {
$tm += $this->title->GetFontHeight($this->img);
}
if( $this->subtitle->t != "" ) {
$tm += $this->subtitle->GetFontHeight($this->img);
}
 
// ...and then take the bottom and top plot margins into account
$height += $tm + $bm + $this->scale->iTopPlotMargin + $this->scale->iBottomPlotMargin;
// Now find the minimum width for the chart required
$fw=$this->scale->day->GetFontWidth($this->img)+4; // Add 2pixel margin on each side
$nd=$this->scale->GetNumberOfDays();
 
// If we display week we must make sure that 7*$fw is enough
// to fit up to 10 characters of the week font (if the week is enabled)
if( $this->scale->IsDisplayWeek() ) {
// Depending on what format the suer has choose we need different amount
// of space
$fsw = strlen($this->scale->week->iLabelFormStr);
if( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
$fsw += 8;
}
elseif( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ) {
$fsw += 7;
}
else {
$fsw += 4;
}
$ww = $fsw*$this->scale->week->GetFontWidth($this->img);
if( 7*$fw < $ww ) {
$fw = ceil($ww/7);
}
}
 
if( !$this->scale->IsDisplayDay() &&
!( ($this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
$this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR) && $this->scale->IsDisplayWeek() ) ) {
// If we don't display the individual days we can shrink the
// scale a little bit. This is a little bit pragmatic at the
// moment and should be re-written to take into account
// a) What scales exactly are shown and
// b) what format do they use so we know how wide we need to
// make each scale text space at minimum.
$fw /= 2;
if( !$this->scale->IsDisplayWeek() ) {
$fw /= 1.8;
}
}
 
// Has the user specified a width or do we need to
// determine it?
if( $this->img->width <= 0 ) {
// Now determine the width for the activity titles column
// This is complicated by the fact that the titles may have
// tabs. In that case we also need to calculate the individual
// tab positions based on the width of the individual columns
$titlewidth = $this->GetMaxLabelWidth();
// Now get the total width taking
// titlewidth, left and rigt margin, dayfont size
// into account
$width = $titlewidth + $nd*$fw + $lm+$rm;
}
else
$width = $this->img->width;
$this->img->CreateImgCanvas($width,$height);
$this->img->SetMargin($lm,$rm,$tm,$bm);
}
// Should we start from the top or just spread the bars out even over the
// available height
$this->scale->SetVertLayout($this->iLayout);
if( $this->iLayout == GANTT_FROMTOP ) {
$maxheight=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
$this->scale->SetVertSpacing($maxheight*(1+$this->iLabelVMarginFactor));
}
// If it hasn't been set find out the maximum line number
if( $this->scale->iVertLines == -1 )
$this->scale->iVertLines = $this->GetBarMaxLineNumber()+1;
$maxwidth=max($this->GetMaxLabelWidth(),$this->scale->tableTitle->GetWidth($this->img));
$this->scale->SetLabelWidth($maxwidth*(1+$this->iLabelHMarginFactor));
if( !$_csim )
$this->StrokePlotArea();
 
$this->scale->Stroke();
 
if( !$_csim )
$this->StrokePlotBox();
$n = count($this->iObj);
for($i=0; $i < $n; ++$i) {
$this->iObj[$i]->SetLabelLeftMargin(round($maxwidth*$this->iLabelHMarginFactor/2));
$this->iObj[$i]->Stroke($this->img,$this->scale);
}
 
if( !$_csim ) {
$this->StrokeConstrains();
$this->StrokeTitles();
$this->footer->Stroke($this->img);
 
// If the filename is given as the special "__handle"
// then the image handler is returned and the image is NOT
// streamed back
if( $aStrokeFileName == _IMG_HANDLER ) {
return $this->img->img;
}
else {
// Finally stream the generated picture
$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
$aStrokeFileName);
}
}
}
 
function StrokeConstrains() {
$n = count($this->iObj);
 
// Stroke all constrains
for($i=0; $i < $n; ++$i) {
$vpos = $this->iObj[$i]->iConstrainRow;
if( $vpos >= 0 ) {
$c1 = $this->iObj[$i]->iConstrainPos;
 
// Find out which object is on the target row
$targetobj = -1;
for( $j=0; $j < $n && $targetobj == -1; ++$j ) {
if( $this->iObj[$j]->iVPos == $vpos ) {
$targetobj = $j;
}
}
if( $targetobj == -1 ) {
JpGraphError::Raise('You have specifed a constrain from row='.
$this->iObj[$i]->iVPos.
' to row='.$vpos.' which does not have any activity.');
exit();
}
$c2 = $this->iObj[$targetobj]->iConstrainPos;
if( count($c1) == 4 && count($c2 ) == 4) {
switch( $this->iObj[$i]->iConstrainType ) {
case CONSTRAIN_ENDSTART:
if( $c1[1] < $c2[1] ) {
$link = new GanttLink($c1[2],$c1[3],$c2[0],$c2[1]);
}
else {
$link = new GanttLink($c1[2],$c1[1],$c2[0],$c2[3]);
}
$link->SetPath(3);
break;
case CONSTRAIN_STARTEND:
if( $c1[1] < $c2[1] ) {
$link = new GanttLink($c1[0],$c1[3],$c2[2],$c2[1]);
}
else {
$link = new GanttLink($c1[0],$c1[1],$c2[2],$c2[3]);
}
$link->SetPath(0);
break;
case CONSTRAIN_ENDEND:
if( $c1[1] < $c2[1] ) {
$link = new GanttLink($c1[2],$c1[3],$c2[2],$c2[1]);
}
else {
$link = new GanttLink($c1[2],$c1[1],$c2[2],$c2[3]);
}
$link->SetPath(1);
break;
case CONSTRAIN_STARTSTART:
if( $c1[1] < $c2[1] ) {
$link = new GanttLink($c1[0],$c1[3],$c2[0],$c2[1]);
}
else {
$link = new GanttLink($c1[0],$c1[1],$c2[0],$c2[3]);
}
$link->SetPath(3);
break;
default:
JpGraphError::Raise('Unknown constrain type specified from row='.
$this->iObj[$i]->iVPos.
' to row='.$vpos);
break;
}
$link->SetColor($this->iObj[$i]->iConstrainColor);
$link->SetArrow($this->iObj[$i]->iConstrainArrowSize,
$this->iObj[$i]->iConstrainArrowType);
$link->Stroke($this->img);
}
}
}
 
}
 
function GetCSIMAreas() {
if( !$this->iHasStroked )
$this->Stroke(_CSIM_SPECIALFILE);
$csim='';
$n = count($this->iObj);
for( $i=$n-1; $i >= 0; --$i )
$csim .= $this->iObj[$i]->GetCSIMArea();
return $csim;
}
}
 
//===================================================
// CLASS TextProperty
// Description: Holds properties for a text
//===================================================
class TextProperty {
var $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10;
var $iColor="black";
var $iShow=true;
var $iText="";
var $iHAlign="left",$iVAlign="bottom";
var $csimtarget='',$csimalt='';
//---------------
// CONSTRUCTOR
function TextProperty($aTxt="") {
$this->iText = $aTxt;
}
//---------------
// PUBLIC METHODS
function Set($aTxt) {
$this->iText = $aTxt;
}
 
function SetCSIMTarget($aTarget,$aAltText='') {
$this->csimtarget=$aTarget;
$this->csimalt=$aAltText;
}
function SetCSIMAlt($aAlt) {
$this->csimalt=$aAlt;
}
 
// Set text color
function SetColor($aColor) {
$this->iColor = $aColor;
}
function HasTabs() {
return substr_count($this->iText,"\t") > 0;
}
// Get number of tabs in string
function GetNbrTabs() {
substr_count($this->iText,"\t");
}
// Set alignment
function Align($aHAlign,$aVAlign="bottom") {
$this->iHAlign=$aHAlign;
$this->iVAlign=$aVAlign;
}
// Specify font
function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
$this->iFFamily = $aFFamily;
$this->iFStyle = $aFStyle;
$this->iFSize = $aFSize;
}
// Get width of text. If text contains several columns separated by
// tabs then return both the total width as well as an array with a
// width for each column.
function GetWidth($aImg,$aUseTabs=false,$aTabExtraMargin=1.1) {
if( strlen($this->iText)== 0 ) return;
$tmp = split("\t",$this->iText);
$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
if( count($tmp) <= 1 || !$aUseTabs ) {
return $aImg->GetTextWidth($this->iText);
}
else {
$tot=0;
for($i=0; $i<count($tmp); ++$i) {
$res[$i] = $aImg->GetTextWidth($tmp[$i]);
$tot += $res[$i]*$aTabExtraMargin;
}
return array($tot,$res);
}
}
// Get total height of text
function GetHeight($aImg) {
$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
return $aImg->GetFontHeight();
}
// Unhide/hide the text
function Show($aShow) {
$this->iShow=$aShow;
}
// Stroke text at (x,y) coordinates. If the text contains tabs then the
// x parameter should be an array of positions to be used for each successive
// tab mark. If no array is supplied then the tabs will be ignored.
function Stroke($aImg,$aX,$aY) {
if( $this->iShow ) {
$aImg->SetColor($this->iColor);
$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
$aImg->SetTextAlign($this->iHAlign,$this->iVAlign);
if( $this->GetNbrTabs() <= 1 || !is_array($aX) ) {
// Get rid of any "\t" characters and stroke string
$aImg->StrokeText($aX,$aY,str_replace("\t"," ",$this->iText));
}
else {
$tmp = split("\t",$this->iText);
$n = min(count($tmp),count($aX));
for($i=0; $i<$n; ++$i) {
$aImg->StrokeText($aX[$i],$aY,$tmp[$i]);
}
}
}
}
}
 
//===================================================
// CLASS HeaderProperty
// Description: Data encapsulating class to hold property
// for each type of the scale headers
//===================================================
class HeaderProperty {
var $iTitleVertMargin=3,$iFFamily=FF_FONT0,$iFStyle=FS_NORMAL,$iFSize=8;
var $iFrameColor="black",$iFrameWeight=1;
var $iShowLabels=true,$iShowGrid=true;
var $iBackgroundColor="white";
var $iWeekendBackgroundColor="lightgray",$iSundayTextColor="red"; // these are only used with day scale
var $iTextColor="black";
var $iLabelFormStr="%d";
var $grid,$iStyle=0;
 
//---------------
// CONSTRUCTOR
function HeaderProperty() {
$this->grid = new LineProperty();
}
 
//---------------
// PUBLIC METHODS
function Show($aShow) {
$this->iShowLabels = $aShow;
}
function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
$this->iFFamily = $aFFamily;
$this->iFStyle = $aFStyle;
$this->iFSize = $aFSize;
}
 
function SetFontColor($aColor) {
$this->iTextColor = $aColor;
}
function GetFontHeight($aImg) {
$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
return $aImg->GetFontHeight();
}
 
function GetFontWidth($aImg) {
$aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
return $aImg->GetFontWidth();
}
function SetStyle($aStyle) {
$this->iStyle = $aStyle;
}
function SetBackgroundColor($aColor) {
$this->iBackgroundColor=$aColor;
}
 
function SetFrameWeight($aWeight) {
$this->iFrameWeight=$aWeight;
}
 
function SetFrameColor($aColor) {
$this->iFrameColor=$aColor;
}
// Only used by day scale
function SetWeekendColor($aColor) {
$this->iWeekendBackgroundColor=$aColor;
}
// Only used by day scale
function SetSundayFontColor($aColor) {
$this->iSundayTextColor=$aColor;
}
function SetTitleVertMargin($aMargin) {
$this->iTitleVertMargin=$aMargin;
}
function SetLabelFormatString($aStr) {
$this->iLabelFormStr=$aStr;
}
}
 
//===================================================
// CLASS GanttScale
// Description: Responsible for calculating and showing
// the scale in a gantt chart. This includes providing methods for
// converting dates to position in the chart as well as stroking the
// date headers (days, week, etc).
//===================================================
class GanttScale {
var $day,$week,$month,$year;
var $divider,$dividerh,$tableTitle;
var $iStartDate=-1,$iEndDate=-1;
// Number of gantt bar position (n.b not necessariliy the same as the number of bars)
// we could have on bar in position 1, and one bar in position 5 then there are two
// bars but the number of bar positions is 5
var $iVertLines=-1;
// The width of the labels (defaults to the widest of all labels)
var $iLabelWidth;
// Out image to stroke the scale to
var $iImg;
var $iTableHeaderBackgroundColor="white",$iTableHeaderFrameColor="black";
var $iTableHeaderFrameWeight=1;
var $iAvailableHeight=-1,$iVertSpacing=-1,$iVertHeaderSize=-1;
var $iDateLocale;
var $iVertLayout=GANTT_EVEN;
var $iTopPlotMargin=10,$iBottomPlotMargin=15;
var $iUsePlotWeekendBackground=true;
var $iWeekStart = 1; // Default to have weekends start on Monday
//---------------
// CONSTRUCTOR
function GanttScale(&$aImg) {
$this->iImg = &$aImg;
$this->iDateLocale = new DateLocale();
$this->day = new HeaderProperty();
$this->day->grid->SetColor("gray");
 
$this->week = new HeaderProperty();
$this->week->SetLabelFormatString("w%d");
$this->week->SetFont(FF_FONT1);
 
$this->month = new HeaderProperty();
$this->month->SetFont(FF_FONT1,FS_BOLD);
 
$this->year = new HeaderProperty();
$this->year->SetFont(FF_FONT1,FS_BOLD);
$this->divider=new LineProperty();
$this->dividerh=new LineProperty();
$this->tableTitle=new TextProperty();
}
//---------------
// PUBLIC METHODS
// Specify what headers should be visible
function ShowHeaders($aFlg) {
$this->day->Show($aFlg & GANTT_HDAY);
$this->week->Show($aFlg & GANTT_HWEEK);
$this->month->Show($aFlg & GANTT_HMONTH);
$this->year->Show($aFlg & GANTT_HYEAR);
 
// Make some default settings of gridlines whihc makes sense
if( $aFlg & GANTT_HWEEK ) {
$this->month->grid->Show(false);
$this->year->grid->Show(false);
}
}
// Should the weekend background stretch all the way down in the plotarea
function UseWeekendBackground($aShow) {
$this->iUsePlotWeekendBackground = $aShow;
}
// Have a range been specified?
function IsRangeSet() {
return $this->iStartDate!=-1 && $this->iEndDate!=-1;
}
// Should the layout be from top or even?
function SetVertLayout($aLayout) {
$this->iVertLayout = $aLayout;
}
// Which locale should be used?
function SetDateLocale($aLocale) {
$this->iDateLocale->Set($aLocale);
}
// Number of days we are showing
function GetNumberOfDays() {
return round(($this->iEndDate-$this->iStartDate)/SECPERDAY)+1;
}
// The width of the actual plot area
function GetPlotWidth() {
$img=$this->iImg;
return $img->width - $img->left_margin - $img->right_margin;
}
 
// Specify the width of the titles(labels) for the activities
// (This is by default set to the minimum width enought for the
// widest title)
function SetLabelWidth($aLabelWidth) {
$this->iLabelWidth=$aLabelWidth;
}
 
// Which day should the week start?
// 0==Sun, 1==Monday, 2==Tuesday etc
function SetWeekStart($aStartDay) {
$this->iWeekStart = $aStartDay % 7;
//Recalculate the startday since this will change the week start
$this->SetRange($this->iStartDate,$this->iEndDate);
}
// Do we show day scale?
function IsDisplayDay() {
return $this->day->iShowLabels;
}
// Do we show week scale?
function IsDisplayWeek() {
return $this->week->iShowLabels;
}
// Do we show month scale?
function IsDisplayMonth() {
return $this->month->iShowLabels;
}
// Do we show year scale?
function IsDisplayYear() {
return $this->year->iShowLabels;
}
 
// Specify spacing (in percent of bar height) between activity bars
function SetVertSpacing($aSpacing) {
$this->iVertSpacing = $aSpacing;
}
 
// Specify scale min and max date either as timestamp or as date strings
// Always round to the nearest week boundary
function SetRange($aMin,$aMax) {
$this->iStartDate = $this->NormalizeDate($aMin);
$this->iEndDate = $this->NormalizeDate($aMax);
}
 
 
// Adjust the start and end date so they fit to beginning/ending
// of the week taking the specified week start day into account.
function AdjustStartEndDay() {
// Get day in week for start and ending date (Sun==0)
$ds=strftime("%w",$this->iStartDate);
$de=strftime("%w",$this->iEndDate);
// We want to start on iWeekStart day. But first we subtract a week
// if the startdate is "behind" the day the week start at.
// This way we ensure that the given start date is always included
// in the range. If we don't do this the nearest correct weekday in the week
// to start at might be later than the start date.
if( $ds < $this->iWeekStart )
$d = strtotime('-7 day',$this->iStartDate);
else
$d = $this->iStartDate;
$adjdate = strtotime(($this->iWeekStart-$ds).' day',$d /*$this->iStartDate*/ );
$this->iStartDate = $adjdate;
// We want to end on the last day of the week
$preferredEndDay = ($this->iWeekStart+6)%7;
if( $preferredEndDay != $de ) {
// Solve equivalence eq: $de + x ~ $preferredDay (mod 7)
$adj = (7+($preferredEndDay - $de)) % 7;
$adjdate = strtotime("+$adj day",$this->iEndDate);
$this->iEndDate = $adjdate;
}
}
 
// Specify background for the table title area (upper left corner of the table)
function SetTableTitleBackground($aColor) {
$this->iTableHeaderBackgroundColor = $aColor;
}
 
///////////////////////////////////////
// PRIVATE Methods
// Determine the height of all the scale headers combined
function GetHeaderHeight() {
$img=$this->iImg;
$height=1;
if( $this->day->iShowLabels ) {
$height += $this->day->GetFontHeight($img);
$height += $this->day->iTitleVertMargin;
}
if( $this->week->iShowLabels ) {
$height += $this->week->GetFontHeight($img);
$height += $this->week->iTitleVertMargin;
}
if( $this->month->iShowLabels ) {
$height += $this->month->GetFontHeight($img);
$height += $this->month->iTitleVertMargin;
}
if( $this->year->iShowLabels ) {
$height += $this->year->GetFontHeight($img);
$height += $this->year->iTitleVertMargin;
}
return $height;
}
// Get width (in pisels) for a single day
function GetDayWidth() {
return ($this->GetPlotWidth()-$this->iLabelWidth+1)/$this->GetNumberOfDays();
}
 
// Nuber of days in a year
function GetNumDaysInYear($aYear) {
if( $this->IsLeap($aYear) )
return 366;
else
return 365;
}
// Get week number
function GetWeekNbr($aDate) {
// We can't use the internal strftime() since it gets the weeknumber
// wrong since it doesn't follow ISO.
// Even worse is that this works differently if we are on a Windows
// or UNIX box (it even differs between UNIX boxes how strftime()
// is natively implemented)
//
// Credit to Nicolas Hoizey <nhoizey@phpheaven.net> for this elegant
// version of Week Nbr calculation.
$day = $this->NormalizeDate($aDate);
/*-------------------------------------------------------------------------
According to ISO-8601 :
"Week 01 of a year is per definition the first week that has the Thursday in this year,
which is equivalent to the week that contains the fourth day of January.
In other words, the first week of a new year is the week that has the majority of its
days in the new year."
Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
day of year = date("z", $day) + 1
offset to thursday = 3 - (date("w", $day) + 6) % 7
first thursday of year = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
week number = (thursday's day of year - first thursday's day of year) / 7 + 1
---------------------------------------------------------------------------*/
$thursday = $day + 60 * 60 * 24 * (3 - (date("w", $day) + 6) % 7); // take week's thursday
$week = 1 + (date("z", $thursday) - (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $thursday)))) % 7) / 7;
return $week;
}
// Is year a leap year?
function IsLeap($aYear) {
// Is the year a leap year?
//$year = 0+date("Y",$aDate);
if( $aYear % 4 == 0)
if( !($aYear % 100 == 0) || ($aYear % 400 == 0) )
return true;
return false;
}
 
// Get current year
function GetYear($aDate) {
return 0+Date("Y",$aDate);
}
// Return number of days in a year
function GetNumDaysInMonth($aMonth,$aYear) {
$days=array(31,28,31,30,31,30,31,31,30,31,30,31);
$daysl=array(31,29,31,30,31,30,31,31,30,31,30,31);
if( $this->IsLeap($aYear))
return $daysl[$aMonth];
else
return $days[$aMonth];
}
// Get day in month
function GetMonthDayNbr($aDate) {
return 0+strftime("%d",$aDate);
}
 
// Get day in year
function GetYearDayNbr($aDate) {
return 0+strftime("%j",$aDate);
}
// Get month number
function GetMonthNbr($aDate) {
return 0+strftime("%m",$aDate);
}
// Translate a date to screen coordinates (horizontal scale)
function TranslateDate($aDate) {
$aDate = $this->NormalizeDate($aDate);
$img=$this->iImg;
return ($aDate-$this->iStartDate)/SECPERDAY*$this->GetDayWidth()+$img->left_margin+$this->iLabelWidth;;
}
 
// Get screen coordinatesz for the vertical position for a bar
function TranslateVertPos($aPos) {
$img=$this->iImg;
$ph=$this->iAvailableHeight;
if( $aPos > $this->iVertLines )
JpGraphError::Raise("Illegal vertical position $aPos");
if( $this->iVertLayout == GANTT_EVEN ) {
// Position the top bar at 1 vert spacing from the scale
return round($img->top_margin + $this->iVertHeaderSize + ($aPos+1)*$this->iVertSpacing);
}
else {
// position the top bar at 1/2 a vert spacing from the scale
return round($img->top_margin + $this->iVertHeaderSize + $this->iTopPlotMargin + ($aPos+1)*$this->iVertSpacing);
}
}
// What is the vertical spacing?
function GetVertSpacing() {
return $this->iVertSpacing;
}
// Convert a date to timestamp
function NormalizeDate($aDate) {
if( is_string($aDate) )
return strtotime($aDate);
elseif( is_int($aDate) || is_float($aDate) )
return $aDate;
else
JpGraphError::Raise("Unknown date format in GanttScale ($aDate).");
}
 
// Stroke the day scale (including gridlines)
function StrokeDays($aYCoord) {
$wdays=$this->iDateLocale->GetDayAbb();
$img=$this->iImg;
$daywidth=$this->GetDayWidth();
$xt=$img->left_margin+$this->iLabelWidth;
$yt=$aYCoord+$img->top_margin;
if( $this->day->iShowLabels ) {
$img->SetFont($this->day->iFFamily,$this->day->iFStyle,$this->day->iFSize);
$xb=$img->width-$img->right_margin;
$yb=$yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
$img->SetColor($this->day->iBackgroundColor);
$img->FilledRectangle($xt,$yt,$xb,$yb);
 
$img->SetColor($this->day->grid->iColor);
$x = $xt;
$img->SetTextAlign("center");
$day = $this->iWeekStart;
//echo "n=".$this->GetNumberOfDays()."<p>";
for($i=0; $i<$this->GetNumberOfDays(); ++$i, $x+=$daywidth, $day += 1,$day %= 7) {
if( $day==6 || $day==0 ) {
$img->PushColor($this->day->iWeekendBackgroundColor);
if( $this->iUsePlotWeekendBackground )
$img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$daywidth,$img->height-$img->bottom_margin);
else
$img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$daywidth,$yb-$this->day->iFrameWeight);
$img->PopColor();
}
if( $day==0 )
$img->PushColor($this->day->iSundayTextColor);
else
$img->PushColor($this->day->iTextColor);
$img->StrokeText(round($x+$daywidth/2+1),
round($yb-$this->day->iTitleVertMargin),
$wdays[$day]);
$img->PopColor();
$img->Line($x,$yt,$x,$yb);
$this->day->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
}
$img->SetColor($this->day->iFrameColor);
$img->SetLineWeight($this->day->iFrameWeight);
$img->Rectangle($xt,$yt,$xb,$yb);
return $yb - $img->top_margin;
}
return $aYCoord;
}
// Stroke week header and grid
function StrokeWeeks($aYCoord) {
$wdays=$this->iDateLocale->GetDayAbb();
$img=$this->iImg;
$weekwidth=$this->GetDayWidth()*7;
$xt=$img->left_margin+$this->iLabelWidth;
$yt=$aYCoord+$img->top_margin;
$img->SetFont($this->week->iFFamily,$this->week->iFStyle,$this->week->iFSize);
$xb=$img->width-$img->right_margin;
$yb=$yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
$week = $this->iStartDate;
$weeknbr=$this->GetWeekNbr($week);
if( $this->week->iShowLabels ) {
$img->SetColor($this->week->iBackgroundColor);
$img->FilledRectangle($xt,$yt,$xb,$yb);
$img->SetColor($this->week->grid->iColor);
$x = $xt;
if( $this->week->iStyle==WEEKSTYLE_WNBR ) {
$img->SetTextAlign("center");
$txtOffset = $weekwidth/2+1;
}
elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
$this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
$this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
$this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
$img->SetTextAlign("left");
$txtOffset = 3;
}
else
JpGraphError::Raise("Unknown formatting style for week.");
for($i=0; $i<$this->GetNumberOfDays()/7; ++$i, $x+=$weekwidth) {
$img->PushColor($this->week->iTextColor);
if( $this->week->iStyle==WEEKSTYLE_WNBR )
$txt = sprintf($this->week->iLabelFormStr,$weeknbr);
elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
$this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR )
$txt = date("j/n",$week);
elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
$this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
$monthnbr = date("n",$week)-1;
$shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
$txt = Date("j",$week)." ".$shortmonth;
}
 
if( $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
$this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
$w = sprintf($this->week->iLabelFormStr,$weeknbr);
$txt .= ' '.$w;
}
$img->StrokeText(round($x+$txtOffset),
round($yb-$this->week->iTitleVertMargin),$txt);
$week = strtotime('+7 day',$week);
$weeknbr = $this->GetWeekNbr($week);
$img->PopColor();
$img->Line($x,$yt,$x,$yb);
$this->week->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
}
$img->SetColor($this->week->iFrameColor);
$img->SetLineWeight($this->week->iFrameWeight);
$img->Rectangle($xt,$yt,$xb,$yb);
return $yb-$img->top_margin;
}
return $aYCoord;
}
// Format the mont scale header string
function GetMonthLabel($aMonthNbr,$year) {
$sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
$ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
switch($this->month->iStyle) {
case MONTHSTYLE_SHORTNAME:
$m=$sn;
break;
case MONTHSTYLE_LONGNAME:
$m=$ln;
break;
case MONTHSTYLE_SHORTNAMEYEAR2:
$m=$sn." '".substr("".$year,2);
break;
case MONTHSTYLE_SHORTNAMEYEAR4:
$m=$sn." ".$year;
break;
case MONTHSTYLE_LONGNAMEYEAR2:
$m=$ln." '".substr("".$year,2);
break;
case MONTHSTYLE_LONGNAMEYEAR4:
$m=$ln." ".$year;
break;
}
return $m;
}
// Stroke month scale and gridlines
function StrokeMonths($aYCoord) {
if( $this->month->iShowLabels ) {
$monthnbr = $this->GetMonthNbr($this->iStartDate)-1;
$img=$this->iImg;
$xt=$img->left_margin+$this->iLabelWidth;
$yt=$aYCoord+$img->top_margin;
$img->SetFont($this->month->iFFamily,$this->month->iFStyle,$this->month->iFSize);
$xb=$img->width-$img->right_margin;
$yb=$yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
$img->SetColor($this->month->iBackgroundColor);
$img->FilledRectangle($xt,$yt,$xb,$yb);
 
$img->SetLineWeight($this->month->grid->iWeight);
$img->SetColor($this->month->iTextColor);
$year = 0+strftime("%Y",$this->iStartDate);
$img->SetTextAlign("center");
if( $this->GetMonthNbr($this->iStartDate) == $this->GetMonthNbr($this->iEndDate)
&& $this->GetYear($this->iStartDate)==$this->GetYear($this->iEndDate) ) {
$monthwidth=$this->GetDayWidth()*($this->GetMonthDayNbr($this->iEndDate) - $this->GetMonthDayNbr($this->iStartDate) + 1);
}
else {
$monthwidth=$this->GetDayWidth()*($this->GetNumDaysInMonth($monthnbr,$year)-$this->GetMonthDayNbr($this->iStartDate)+1);
}
// Is it enough space to stroke the first month?
$monthName = $this->GetMonthLabel($monthnbr,$year);
if( $monthwidth >= 1.2*$img->GetTextWidth($monthName) ) {
$img->SetColor($this->month->iTextColor);
$img->StrokeText(round($xt+$monthwidth/2+1),
round($yb-$this->month->iTitleVertMargin),
$monthName);
}
$x = $xt + $monthwidth;
while( $x < $xb ) {
$img->SetColor($this->month->grid->iColor);
$img->Line($x,$yt,$x,$yb);
$this->month->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
$monthnbr++;
if( $monthnbr==12 ) {
$monthnbr=0;
$year++;
}
$monthName = $this->GetMonthLabel($monthnbr,$year);
$monthwidth=$this->GetDayWidth()*$this->GetNumDaysInMonth($monthnbr,$year);
if( $x + $monthwidth < $xb )
$w = $monthwidth;
else
$w = $xb-$x;
if( $w >= 1.2*$img->GetTextWidth($monthName) ) {
$img->SetColor($this->month->iTextColor);
$img->StrokeText(round($x+$w/2+1),
round($yb-$this->month->iTitleVertMargin),$monthName);
}
$x += $monthwidth;
}
$img->SetColor($this->month->iFrameColor);
$img->SetLineWeight($this->month->iFrameWeight);
$img->Rectangle($xt,$yt,$xb,$yb);
return $yb-$img->top_margin;
}
return $aYCoord;
}
 
// Stroke year scale and gridlines
function StrokeYears($aYCoord) {
if( $this->year->iShowLabels ) {
$year = $this->GetYear($this->iStartDate);
$img=$this->iImg;
$xt=$img->left_margin+$this->iLabelWidth;
$yt=$aYCoord+$img->top_margin;
$img->SetFont($this->year->iFFamily,$this->year->iFStyle,$this->year->iFSize);
$xb=$img->width-$img->right_margin;
$yb=$yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
$img->SetColor($this->year->iBackgroundColor);
$img->FilledRectangle($xt,$yt,$xb,$yb);
$img->SetLineWeight($this->year->grid->iWeight);
$img->SetTextAlign("center");
if( $year == $this->GetYear($this->iEndDate) )
$yearwidth=$this->GetDayWidth()*($this->GetYearDayNbr($this->iEndDate)-$this->GetYearDayNbr($this->iStartDate)+1);
else
$yearwidth=$this->GetDayWidth()*($this->GetNumDaysInYear($year)-$this->GetYearDayNbr($this->iStartDate)+1);
// The space for a year must be at least 20% bigger than the actual text
// so we allow 10% margin on each side
if( $yearwidth >= 1.20*$img->GetTextWidth("".$year) ) {
$img->SetColor($this->year->iTextColor);
$img->StrokeText(round($xt+$yearwidth/2+1),
round($yb-$this->year->iTitleVertMargin),
$year);
}
$x = $xt + $yearwidth;
while( $x < $xb ) {
$img->SetColor($this->year->grid->iColor);
$img->Line($x,$yt,$x,$yb);
$this->year->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
$year += 1;
$yearwidth=$this->GetDayWidth()*$this->GetNumDaysInYear($year);
if( $x + $yearwidth < $xb )
$w = $yearwidth;
else
$w = $xb-$x;
if( $w >= 1.2*$img->GetTextWidth("".$year) ) {
$img->SetColor($this->year->iTextColor);
$img->StrokeText(round($x+$w/2+1),
round($yb-$this->year->iTitleVertMargin),
$year);
}
$x += $yearwidth;
}
$img->SetColor($this->year->iFrameColor);
$img->SetLineWeight($this->year->iFrameWeight);
$img->Rectangle($xt,$yt,$xb,$yb);
return $yb-$img->top_margin;
}
return $aYCoord;
}
// Stroke table title (upper left corner)
function StrokeTableHeaders($aYBottom) {
$img=$this->iImg;
$xt=$img->left_margin;
$yt=$img->top_margin;
$xb=$xt+$this->iLabelWidth;
$yb=$aYBottom+$img->top_margin;
$img->SetColor($this->iTableHeaderBackgroundColor);
$img->FilledRectangle($xt,$yt,$xb,$yb);
$this->tableTitle->Align("center","center");
$this->tableTitle->Stroke($img,$xt+($xb-$xt)/2+1,$yt+($yb-$yt)/2);
$img->SetColor($this->iTableHeaderFrameColor);
$img->SetLineWeight($this->iTableHeaderFrameWeight);
$img->Rectangle($xt,$yt,$xb,$yb);
// Draw the vertical dividing line
$this->divider->Stroke($img,$xb,$yt,$xb,$img->height-$img->bottom_margin);
// Draw the horizontal dividing line
$this->dividerh->Stroke($img,$xt,$yb,$img->width-$img->right_margin,$yb);
}
 
// Main entry point to stroke scale
function Stroke() {
if( !$this->IsRangeSet() )
JpGraphError::Raise("Gantt scale has not been specified.");
$img=$this->iImg;
// Stroke all headers. Aa argument we supply the offset from the
// top which depends on any previous headers
$offy=$this->StrokeYears(0);
$offm=$this->StrokeMonths($offy);
$offw=$this->StrokeWeeks($offm);
$offd=$this->StrokeDays($offw);
 
// We stroke again in case days also have gridlines that may have
// overwritten the weeks gridline (or month/year). It may seem that we should have logic
// in the days routine instead but this is much easier and wont make to much
// of an performance impact.
$this->StrokeWeeks($offm);
$this->StrokeMonths($offy);
$this->StrokeYears(0);
$this->StrokeTableHeaders($offd);
// Now we can calculate the correct scaling factor for each vertical position
$this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;
$this->iVertHeaderSize = $offd;
if( $this->iVertSpacing == -1 )
$this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
}
}
 
//===================================================
// CLASS GanttPlotObject
// The common signature for a Gantt object
//===================================================
class GanttPlotObject {
var $iVPos=0; // Vertical position
var $iLabelLeftMargin=2; // Title margin
var $iStart=""; // Start date
var $title,$caption;
var $iCaptionMargin=5;
var $csimarea='',$csimtarget='',$csimalt='';
var $iConstrainType=CONSTRAIN_ENDSTART,$iConstrainRow=-1;
var $iConstrainColor='black',$iConstrainArrowSize=ARROW_S2,$iConstrainArrowType=ARROWT_SOLID;
var $iConstrainPos=array();
function GanttPlotObject() {
$this->title = new TextProperty();
$this->title->Align("left","center");
$this->caption = new TextProperty();
}
 
function GetCSIMArea() {
return $this->csimarea;
}
 
function SetCSIMTarget($aTarget,$aAltText='') {
$this->csimtarget=$aTarget;
$this->csimalt=$aAltText;
}
function SetCSIMAlt($aAlt) {
$this->csimalt=$aAlt;
}
 
function SetConstrain($aRow,$aType,$aColor='black',$aArrowSize=ARROW_S2,$aArrowType=ARROWT_SOLID) {
$this->iConstrainRow = $aRow;
$this->iConstrainType = $aType;
$this->iConstrainColor = $aColor;
$this->iConstrainArrowSize = $aArrowSize;
$this->iConstrainArrowType = $aArrowType;
}
 
function SetConstrainPos($xt,$yt,$xb,$yb) {
$this->iConstrainPos = array($xt,$yt,$xb,$yb);
}
 
function GetConstrain() {
return array($this->iConstrainRow,$this->iConstrainType);
}
function GetMinDate() {
return $this->iStart;
}
 
function GetMaxDate() {
return $this->iStart;
}
function SetCaptionMargin($aMarg) {
$this->iCaptionMargin=$aMarg;
}
 
function GetAbsHeight($aImg) {
return 0;
}
function GetLineNbr() {
return $this->iVPos;
}
 
function SetLabelLeftMargin($aOff) {
$this->iLabelLeftMargin=$aOff;
}
}
 
//===================================================
// CLASS Progress
// Holds parameters for the progress indicator
// displyed within a bar
//===================================================
class Progress {
var $iProgress=-1, $iColor="black", $iFillColor='black';
var $iPattern=GANTT_SOLID;
var $iDensity=98, $iHeight=0.65;
function Set($aProg) {
if( $aProg < 0.0 || $aProg > 1.0 )
JpGraphError::Raise("Progress value must in range [0, 1]");
$this->iProgress = $aProg;
}
 
function SetPattern($aPattern,$aColor="blue",$aDensity=98) {
$this->iPattern = $aPattern;
$this->iColor = $aColor;
$this->iDensity = $aDensity;
}
 
function SetFillColor($aColor) {
$this->iFillColor = $aColor;
}
function SetHeight($aHeight) {
$this->iHeight = $aHeight;
}
}
 
//===================================================
// CLASS GanttBar
// Responsible for formatting individual gantt bars
//===================================================
class GanttBar extends GanttPlotObject {
var $iEnd;
var $iHeightFactor=0.5;
var $iFillColor="white",$iFrameColor="black";
var $iShadow=false,$iShadowColor="darkgray",$iShadowWidth=1,$iShadowFrame="black";
var $iPattern=GANTT_RDIAG,$iPatternColor="blue",$iPatternDensity=95;
var $leftMark,$rightMark;
var $progress;
//---------------
// CONSTRUCTOR
function GanttBar($aPos,$aLabel,$aStart,$aEnd,$aCaption="",$aHeightFactor=0.6) {
parent::GanttPlotObject();
$this->iStart = $aStart;
// Is the end date given as a date or as number of days added to start date?
if( is_string($aEnd) )
$this->iEnd = strtotime($aEnd)+SECPERDAY;
elseif(is_int($aEnd) || is_float($aEnd) )
$this->iEnd = strtotime($aStart)+round($aEnd*SECPERDAY);
$this->iVPos = $aPos;
$this->iHeightFactor = $aHeightFactor;
$this->title->Set($aLabel);
$this->caption = new TextProperty($aCaption);
$this->caption->Align("left","center");
$this->leftMark =new PlotMark();
$this->leftMark->Hide();
$this->rightMark=new PlotMark();
$this->rightMark->Hide();
$this->progress = new Progress();
}
//---------------
// PUBLIC METHODS
function SetShadow($aShadow=true,$aColor="gray") {
$this->iShadow=$aShadow;
$this->iShadowColor=$aColor;
}
function GetMaxDate() {
return $this->iEnd;
}
function SetHeight($aHeight) {
$this->iHeightFactor = $aHeight;
}
 
function SetColor($aColor) {
$this->iFrameColor = $aColor;
}
 
function SetFillColor($aColor) {
$this->iFillColor = $aColor;
}
 
function GetAbsHeight($aImg) {
if( is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show ) {
$m=-1;
if( is_int($this->iHeightFactor) )
$m = $this->iHeightFactor;
if( $this->leftMark->show )
$m = max($m,$this->leftMark->width*2);
if( $this->rightMark->show )
$m = max($m,$this->rightMark->width*2);
return $m;
}
else
return -1;
}
function SetPattern($aPattern,$aColor="blue",$aDensity=95) {
$this->iPattern = $aPattern;
$this->iPatternColor = $aColor;
$this->iPatternDensity = $aDensity;
}
 
function Stroke($aImg,$aScale) {
$factory = new RectPatternFactory();
$prect = $factory->Create($this->iPattern,$this->iPatternColor);
$prect->SetDensity($this->iPatternDensity);
 
// If height factor is specified as a float between 0,1 then we take it as meaning
// percetage of the scale width between horizontal line.
// If it is an integer > 1 we take it to mean the absolute height in pixels
if( $this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1)
$vs = $aScale->GetVertSpacing()*$this->iHeightFactor;
elseif(is_int($this->iHeightFactor) && $this->iHeightFactor>2 && $this->iHeightFactor<200)
$vs = $this->iHeightFactor;
else
JpGraphError::Raise("Specified height (".$this->iHeightFactor.") for gantt bar is out of range.");
// Clip date to min max dates to show
$st = $aScale->NormalizeDate($this->iStart);
$en = $aScale->NormalizeDate($this->iEnd);
 
$limst = max($st,$aScale->iStartDate);
$limen = min($en,$aScale->iEndDate+SECPERDAY);
$xt = round($aScale->TranslateDate($limst));
$xb = round($aScale->TranslateDate($limen)-1);
$yt = round($aScale->TranslateVertPos($this->iVPos)-$vs-($aScale->GetVertSpacing()/2-$vs/2));
$yb = round($aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2-$vs/2));
$middle = round($yt+($yb-$yt)/2);
$this->title->Stroke($aImg,$aImg->left_margin+$this->iLabelLeftMargin,$middle);
 
// CSIM for title
if( $this->title->csimtarget != '' ) {
$title_xt = $aImg->left_margin+$this->iLabelLeftMargin;
$title_xb = $title_xt + $this->title->GetWidth($aImg);
 
$coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
$this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget."\"";
if( $this->title->csimalt != '' ) {
$tmp = $this->title->csimalt;
$this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimarea .= ">\n";
}
 
// Check if the bar is totally outside the current scale range
if( $en < $aScale->iStartDate+SECPERDAY || $st > $aScale->iEndDate )
return;
 
// Remember the positions for the bar
$this->SetConstrainPos($xt,$yt,$xb,$yb);
$prect->ShowFrame(false);
$prect->SetBackground($this->iFillColor);
if( $this->iShadow ) {
$aImg->SetColor($this->iFrameColor);
$aImg->ShadowRectangle($xt,$yt,$xb,$yb,$this->iFillColor,$this->iShadowWidth,$this->iShadowColor);
$prect->SetPos(new Rectangle($xt+1,$yt+1,$xb-$xt-$this->iShadowWidth-2,$yb-$yt-$this->iShadowWidth-2));
$prect->Stroke($aImg);
}
else {
$prect->SetPos(new Rectangle($xt,$yt,$xb-$xt+1,$yb-$yt+1));
$prect->Stroke($aImg);
$aImg->SetColor($this->iFrameColor);
$aImg->Rectangle($xt,$yt,$xb,$yb);
}
 
// CSIM for bar
if( $this->csimtarget != '' ) {
 
$coords = "$xt,$yt,$xb,$yt,$xb,$yb,$xt,$yb";
$this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
$this->csimtarget."\"";
if( $this->csimalt != '' ) {
$tmp = $this->csimalt;
$this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimarea .= ">\n";
}
 
// Draw progress bar inside activity bar
if( $this->progress->iProgress > 0 ) {
$xtp = $aScale->TranslateDate($st);
$xbp = $aScale->TranslateDate($en);
$len = ($xbp-$xtp)*$this->progress->iProgress;
 
$endpos = $xtp+$len;
if( $endpos > $xt ) {
$len -= ($xt-$xtp);
 
// Make sure that the progess bar doesn't extend over the end date
if( $xtp+$len-1 > $xb )
$len = $xb - $xtp + 1;
if( $xtp < $xt )
$xtp = $xt;
$prog = $factory->Create($this->progress->iPattern,$this->progress->iColor);
$prog->SetDensity($this->progress->iDensity);
$prog->SetBackground($this->progress->iFillColor);
$barheight = ($yb-$yt+1);
if( $this->iShadow )
$barheight -= $this->iShadowWidth;
$progressheight = floor($barheight*$this->progress->iHeight);
$marg = ceil(($barheight-$progressheight)/2);
$pos = new Rectangle($xtp,$yt + $marg, $len,$barheight-2*$marg);
$prog->SetPos($pos);
$prog->Stroke($aImg);
}
}
// We don't plot the end mark if the bar has been capped
if( $limst == $st ) {
$y = $middle;
// We treat the RIGHT and LEFT triangle mark a little bi
// special so that these marks are placed right under the
// bar.
if( $this->leftMark->GetType() == MARK_LEFTTRIANGLE ) {
$y = $yb ;
}
$this->leftMark->Stroke($aImg,$xt,$y);
}
if( $limen == $en ) {
$y = $middle;
// We treat the RIGHT and LEFT triangle mark a little bi
// special so that these marks are placed right under the
// bar.
if( $this->rightMark->GetType() == MARK_RIGHTTRIANGLE ) {
$y = $yb ;
}
$this->rightMark->Stroke($aImg,$xb,$y);
$margin = $this->iCaptionMargin;
if( $this->rightMark->show )
$margin += $this->rightMark->GetWidth();
$this->caption->Stroke($aImg,$xb+$margin,$middle);
}
}
}
 
//===================================================
// CLASS MileStone
// Responsible for formatting individual milestones
//===================================================
class MileStone extends GanttPlotObject {
var $mark;
//---------------
// CONSTRUCTOR
function MileStone($aVPos,$aLabel,$aDate,$aCaption="") {
GanttPlotObject::GanttPlotObject();
$this->caption->Set($aCaption);
$this->caption->Align("left","center");
$this->caption->SetFont(FF_FONT1,FS_BOLD);
$this->title->Set($aLabel);
$this->title->SetColor("darkred");
$this->mark = new PlotMark();
$this->mark->SetWidth(10);
$this->mark->SetType(MARK_DIAMOND);
$this->mark->SetColor("darkred");
$this->mark->SetFillColor("darkred");
$this->iVPos = $aVPos;
$this->iStart = $aDate;
}
//---------------
// PUBLIC METHODS
function GetAbsHeight($aImg) {
return max($this->title->GetHeight($aImg),$this->mark->GetWidth());
}
function Stroke($aImg,$aScale) {
// Put the mark in the middle at the middle of the day
$d = $aScale->NormalizeDate($this->iStart)+SECPERDAY/2;
$x = $aScale->TranslateDate($d);
$y = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2);
$this->title->Stroke($aImg,$aImg->left_margin+$this->iLabelLeftMargin,$y);
 
// CSIM for title
if( $this->title->csimtarget != '' ) {
$title_xt = $aImg->left_margin+$this->iLabelLeftMargin;
$title_xb = $title_xt + $this->title->GetWidth($aImg);
$yt = round($y - $this->title->GetHeight($aImg)/2);
$yb = round($y + $this->title->GetHeight($aImg)/2);
$coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
$this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget."\"";
if( $this->title->csimalt != '' ) {
$tmp = $this->title->csimalt;
$this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimarea .= ">\n";
}
 
 
if( $d < $aScale->iStartDate || $d > $aScale->iEndDate )
return;
 
// Remember the coordinates for any constrains linking to
// this milestone
$w = $this->mark->GetWidth()/2;
$this->SetConstrainPos($x,round($y-$w),$x,round($y+$w));
// Setup CSIM
if( $this->csimtarget != '' ) {
$this->mark->SetCSIMTarget( $this->csimtarget );
$this->mark->SetCSIMAlt( $this->csimalt );
}
$this->mark->Stroke($aImg,$x,$y);
$this->caption->Stroke($aImg,$x+$this->mark->width/2+$this->iCaptionMargin,$y);
 
$this->csimarea .= $this->mark->GetCSIMAreas();
}
}
 
 
//===================================================
// CLASS GanttVLine
// Responsible for formatting individual milestones
//===================================================
 
class GanttVLine extends GanttPlotObject {
 
var $iLine,$title_margin=3;
var $iDayOffset=1; // Defult to right edge of day
//---------------
// CONSTRUCTOR
function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight=3,$aStyle="dashed") {
GanttPlotObject::GanttPlotObject();
$this->iLine = new LineProperty();
$this->iLine->SetColor($aColor);
$this->iLine->SetWeight($aWeight);
$this->iLine->SetStyle($aStyle);
$this->iStart = $aDate;
$this->title->Set($aTitle);
}
 
//---------------
// PUBLIC METHODS
 
function SetDayOffset($aOff=0.5) {
if( $aOff < 0.0 || $aOff > 1.0 )
JpGraphError::Raise("Offset for vertical line must be in range [0,1]");
$this->iDayOffset = $aOff;
}
function SetTitleMargin($aMarg) {
$this->title_margin = $aMarg;
}
function Stroke($aImg,$aScale) {
$d = $aScale->NormalizeDate($this->iStart)+$this->iDayOffset*SECPERDAY;
 
if( $d < $aScale->iStartDate || $d > $aScale->iEndDate )
return;
 
$x = $aScale->TranslateDate($d);
$y1 = $aScale->iVertHeaderSize+$aImg->top_margin;
$y2 = $aImg->height - $aImg->bottom_margin;
$this->iLine->Stroke($aImg,$x,$y1,$x,$y2);
$this->title->Align("center","top");
$this->title->Stroke($aImg,$x,$y2+$this->title_margin);
}
}
 
//===================================================
// CLASS LinkArrow
// Handles the drawing of a an arrow
//===================================================
class LinkArrow {
var $ix,$iy;
var $isizespec = array(
array(2,3),array(3,5),array(3,8),array(6,15),array(8,22));
var $iDirection=ARROW_DOWN,$iType=ARROWT_SOLID,$iSize=ARROW_S2;
var $iColor='black';
 
function LinkArrow($x,$y,$aDirection,$aType=ARROWT_SOLID,$aSize=ARROW_S2) {
$this->iDirection = $aDirection;
$this->iType = $aType;
$this->iSize = $aSize;
$this->ix = $x;
$this->iy = $y;
}
function SetColor($aColor) {
$this->iColor = $aColor;
}
 
function SetSize($aSize) {
$this->iSize = $aSize;
}
 
function SetType($aType) {
$this->iType = $aType;
}
 
function Stroke($aImg) {
list($dx,$dy) = $this->isizespec[$this->iSize];
$x = $this->ix;
$y = $this->iy;
switch ( $this->iDirection ) {
case ARROW_DOWN:
$c = array($x,$y,$x-$dx,$y-$dy,$x+$dx,$y-$dy,$x,$y);
break;
case ARROW_UP:
$c = array($x,$y,$x-$dx,$y+$dy,$x+$dx,$y+$dy,$x,$y);
break;
case ARROW_LEFT:
$c = array($x,$y,$x+$dy,$y-$dx,$x+$dy,$y+$dx,$x,$y);
break;
case ARROW_RIGHT:
$c = array($x,$y,$x-$dy,$y-$dx,$x-$dy,$y+$dx,$x,$y);
break;
default:
JpGraphError::Raise('Unknown arrow direction for link.');
die();
break;
}
$aImg->SetColor($this->iColor);
switch( $this->iType ) {
case ARROWT_SOLID:
$aImg->FilledPolygon($c);
break;
case ARROWT_OPEN:
$aImg->Polygon($c);
break;
default:
JpGraphError::Raise('Unknown arrow type for link.');
die();
break;
}
}
}
 
//===================================================
// CLASS GanttLink
// Handles the drawing of a link line between 2 points
//===================================================
 
class GanttLink {
var $iArrowType='';
var $ix1,$ix2,$iy1,$iy2;
var $iPathType=2,$iPathExtend=15;
var $iColor='black',$iWeight=1;
var $iArrowSize=ARROW_S2,$iArrowType=ARROWT_SOLID;
 
function GanttLink($x1=0,$y1=0,$x2=0,$y2=0) {
$this->ix1 = $x1;
$this->ix2 = $x2;
$this->iy1 = $y1;
$this->iy2 = $y2;
}
 
function SetPos($x1,$y1,$x2,$y2) {
$this->ix1 = $x1;
$this->ix2 = $x2;
$this->iy1 = $y1;
$this->iy2 = $y2;
}
 
function SetPath($aPath) {
$this->iPathType = $aPath;
}
 
function SetColor($aColor) {
$this->iColor = $aColor;
}
 
function SetArrow($aSize,$aType=ARROWT_SOLID) {
$this->iArrowSize = $aSize;
$this->iArrowType = $aType;
}
function SetWeight($aWeight) {
$this->iWeight = $aWeight;
}
 
function Stroke($aImg) {
// The way the path for the arrow is constructed is partly based
// on some heuristics. This is not an exact science but draws the
// path in a way that, for me, makes esthetic sence. For example
// if the start and end activities are very close we make a small
// detour to endter the target horixontally. If there are more
// space between axctivities then no suh detour is made and the
// target is "hit" directly vertical. I have tried to keep this
// simple. no doubt this could become almost infinitive complex
// and have some real AI. Feel free to modify this.
// This will no-doubt be tweaked as times go by. One design aim
// is to avoid having the user choose what types of arrow
// he wants.
 
// The arrow is drawn between (x1,y1) to (x2,y2)
$x1 = $this->ix1 ;
$x2 = $this->ix2 ;
$y1 = $this->iy1 ;
$y2 = $this->iy2 ;
 
// Depending on if the target is below or above we have to
// handle thi different.
if( $y2 > $y1 ) {
$arrowtype = ARROW_DOWN;
$midy = round(($y2-$y1)/2+$y1);
if( $x2 > $x1 ) {
switch ( $this->iPathType ) {
case 0:
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
break;
case 1:
case 2:
case 3:
$c = array($x1,$y1,$x2,$y1,$x2,$y2);
break;
default:
JpGraphError::Raise('Internal error: Unknown path type (='.$this->iPathType .') specified for link.');
exit(1);
break;
}
}
else {
switch ( $this->iPathType ) {
case 0:
case 1:
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
break;
case 2:
// Always extend out horizontally a bit from the first point
// If we draw a link back in time (end to start) and the bars
// are very close we also change the path so it comes in from
// the left on the activity
$c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
$x1+$this->iPathExtend,$midy,
$x2,$midy,$x2,$y2);
break;
case 3:
if( $y2-$midy < 6 ) {
$c = array($x1,$y1,$x1,$midy,
$x2-$this->iPathExtend,$midy,
$x2-$this->iPathExtend,$y2,
$x2,$y2);
$arrowtype = ARROW_RIGHT;
}
else {
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
}
break;
default:
JpGraphError::Raise('Internal error: Unknown path type specified for link.');
exit(1);
break;
}
}
$arrow = new LinkArrow($x2,$y2,$arrowtype);
}
else {
// Y2 < Y1
$arrowtype = ARROW_UP;
$midy = round(($y1-$y2)/2+$y2);
if( $x2 > $x1 ) {
switch ( $this->iPathType ) {
case 0:
case 1:
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
break;
case 3:
if( $midy-$y2 < 8 ) {
$arrowtype = ARROW_RIGHT;
$c = array($x1,$y1,$x1,$y2,$x2,$y2);
}
else {
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
}
break;
default:
JpGraphError::Raise('Internal error: Unknown path type specified for link.');
break;
}
}
else {
switch ( $this->iPathType ) {
case 0:
case 1:
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
break;
case 2:
// Always extend out horizontally a bit from the first point
$c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
$x1+$this->iPathExtend,$midy,
$x2,$midy,$x2,$y2);
break;
case 3:
if( $midy-$y2 < 16 ) {
$arrowtype = ARROW_RIGHT;
$c = array($x1,$y1,$x1,$midy,$x2-$this->iPathExtend,$midy,
$x2-$this->iPathExtend,$y2,
$x2,$y2);
}
else {
$c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
}
break;
default:
JpGraphError::Raise('Internal error: Unknown path type specified for link.');
exit(1);
break;
}
}
$arrow = new LinkArrow($x2,$y2,$arrowtype);
}
$aImg->SetColor($this->iColor);
$aImg->SetLineWeight($this->iWeight);
$aImg->Polygon($c);
$aImg->SetLineWeight(1);
$arrow->SetColor($this->iColor);
$arrow->SetSize($this->iArrowSize);
$arrow->SetType($this->iArrowType);
$arrow->Stroke($aImg);
}
}
 
// <EOF>
?>
/trunk/api/jpgraph_1.12.2/imgdata_squares.inc
New file
0,0 → 1,152
<?php
//=======================================================================
// File: IMGDATA_SQUARES.INC
// Description: Base64 encoded images for squares
// Created: 2003-03-20
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: imgdata_squares.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
class ImgData_Squares extends ImgData {
var $name = 'Squares';
var $an = array(MARK_IMG_SQUARE =>'imgdata');
var $colors = array('bluegreen','blue','green',
'lightblue','orange','purple','red','yellow');
var $index = array('bluegreen' =>2,'blue'=>5,'green'=>6,
'lightblue'=>0,'orange'=>7,'purple'=>4,'red'=>3,'yellow'=>1);
var $maxidx = 7 ;
var $imgdata ;
 
function ImgData_Squares () {
//==========================================================
//sq_lblue.png
//==========================================================
$this->imgdata[0][0]= 362 ;
$this->imgdata[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMLFgojiPx/ygAAAPdJREFUeNpj/P377+kzHx89/c'.
'VAHNBQ5VBX52HavPWWjg6nnDQbkXoUFTnnL7zD9PPXrz17HxCj'.
'E6Jn6fL7H7/+ZWJgYCBGJ7IeBgYGJogofp1oehDa8OjE1IOiDa'.
'tOrHoYGBhY0NwD0enirMDAwMDFxYRVD7ptyDrNTAU0NXix6sGu'.
'jYGBgZOT9e/f/0xMjFyczFgVsGAKCfBza2kKzpl3hIuT1c9Xb/'.
'PW58/foKchJqx6tmy98vbjj8cvPm/afMnXW1JShA2fNmQ9EBFc'.
'Opnw6MGjkwm/Hlw6mQjqwaqTiRg9mDoZv//4M2/+UYJ64EBWgj'.
'cm2hwA8l24oNDl+DMAAAAASUVORK5CYII=' ;
 
//==========================================================
//sq_yellow.png
//==========================================================
$this->imgdata[1][0]= 338 ;
$this->imgdata[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAWl'.
'BMVEX////+/+H+/9/9/9v8/8P8/8H8/7v8/7n6/4P5/335/3n5'.
'/3X4/1f4/1P3/031/w30/wn0/wPt+ADp9ADm8ADk7gDc5gDa5A'.
'DL1ADFzgCwuACqsgClrABzeAC9M0MzAAAAAWJLR0QAiAUdSAAA'.
'AAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEDlOgDj'.
'EAAAB+SURBVHjaVcpbCsQgDEDRGERGKopjDa2a/W9zfLWj9/Nw'.
'Ac21ZRBOtZlRN9ApzSYFaDUj79KIorRDbJNO9bN/GUSh2ZRJFJ'.
'S18iorURBiyksO8buT0zkfYaUqzI91ckfhWhoGXTLzsDjI68Sz'.
'pGMjrzPzauA/iXk1AtykmvgBC8UcWUdc9HkAAAAASUVORK5CYI'.
'I=' ;
 
//==========================================================
//sq_blgr.png
//==========================================================
$this->imgdata[2][0]= 347 ;
$this->imgdata[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAZl'.
'BMVEX////0+vv0+vrz+fry+frv+Png7e/d7e/a6+zY6+250tSz'.
'0tSyztCtztGM0NWIz9SDzdNfsLVcrrRZrbJOp61MpqtIr7dHn6'.
'RErrZArLQ6q7M2g4kygYcsp68npa4ctr8QZ20JnqepKsl4AAAA'.
'AWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU'.
'1FB9MDCxYEByp8tpUAAAB7SURBVHjaVcjRFoIgDADQWZpWJpjY'.
'MsnG//9kzIFn3McLzfArDA3MndFjrhvgfDHFBEB9pt0CVzwrY3'.
'n2yicjhY4vTSp0nbXtN+hCV53SHDWe61dZY+/9463r2XuifHAM'.
'0SoH+6xEcovUlCfefeFSIwfTTQ3fB+pi4lV/bTIgvmaA7a0AAA'.
'AASUVORK5CYII=' ;
 
//==========================================================
//sq_red.png
//==========================================================
$this->imgdata[3][0]= 324 ;
$this->imgdata[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAXV'.
'BMVEX////++Pn99/j99ff99fb98/X98/T98PL55uj43+P24+bw'.
'kKPvjaHviJ3teJHpxMnoL2Pjs73WW3rWNljVWXnUVnbUK1DTJk'.
'3SUHPOBz/KQmmxPVmuOFasNFOeIkWVka/fAAAAAWJLR0QAiAUd'.
'SAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEHd'.
'ceT+8AAABtSURBVHjaVchbAkMwEAXQq6i3VrQiQfa/zDYTw8z5'.
'PCjGt9JVWFt1XWPh1fWNdfDy+tq6WPfRUPENNKnSnXNWPB4uv2'.
'b54nSZ8jHrMtOxvWZZZtpD4KP6xLkO9/AhzhaCOMhJh68cOjzV'.
'/K/4Ac2cG+nBcaRuAAAAAElFTkSuQmCC' ;
 
//==========================================================
//sq_pink.png
//==========================================================
$this->imgdata[4][0]= 445 ;
$this->imgdata[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAApV'.
'BMVEX////6+Pz69/v49Pr38/r17/jr4+/l3Onj2efh1ua/L+i+'.
'q8m+Lue9Lua8qsS8LuW8LeS7pca5LOG4LN+2Y9O2YNW1ZdO1Kt'.
'y0atC0aNGzb82zbc6zKtuzKdqycsuwa8qtJtOISZ2GRpuFN6GE'.
'NqCDQpmCMZ+BPpd/LJ1/K519S5B9Jpx9Jpt9JZt6RY11BJZ1BJ'.
'V0BJV0BJRzBJNvNoRtIoJUEmdZ/XbrAAAAAWJLR0QAiAUdSAAA'.
'AAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYDF3iKMD'.
'YAAACeSURBVHjaVczbEoIgGARgCiMtrexoWpaa2FHUgvd/tH4Y'.
'BnEvv9ldhNPradPnnGBUTtPDzMRPSIF46SaBoR25dYjz3I20Lb'.
'ek6BgQz73Il7KKpSgCO0pTHU0886J1sCe0ZYbALjGhjFnEM2es'.
'VhZVI4d+B1QtfnV47ywCEaKeP/p7JdLejSYt0j6NIiOq1wJZIs'.
'QTDA0ELHwhPBCwyR/Cni9cOmzJtwAAAABJRU5ErkJggg==' ;
 
//==========================================================
//sq_blue.png
//==========================================================
$this->imgdata[5][0]= 283 ;
$this->imgdata[5][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAQl'.
'BMVEX////4+fz39/z19vvy8vru7/ni4+7g4fHW1ue8vteXmt6B'.
'hdhiZ7FQVaZETcxCSJo1Oq4zNoMjKakhJHcKFaMEC2jRVYdWAA'.
'AAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0'.
'SU1FB9MDCxYDN0PkEP4AAABfSURBVHjaVchHAoAgDATAVcCCIF'.
'j4/1elJEjmOFDHKVgDv4iz640gLs+LMF6ZUv/VqcXXplU7Gqpy'.
'PFzBT5qml9NzlOX259riWHlS4kOffviHD8PQYZx2EFMPRkw+9Q'.
'FSnRPeWEDzKAAAAABJRU5ErkJggg==' ;
 
//==========================================================
//sq_green.png
//==========================================================
$this->imgdata[6][0]= 325 ;
$this->imgdata[6][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAXV'.
'BMVEX////2+vX1+vX1+fT0+fPz+PPx9/Dv9u7u9e3h7uHe697a'.
'6dnO2s3I1sa10LOvza2ay5aEwYBWlE9TqE5Tkk1RkEpMrUJMg0'.
'hKiUNGpEFBojw8oTcsbScaYBMWlwmMT0NtAAAAAWJLR0QAiAUd'.
'SAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEFd'.
'nFx90AAABuSURBVHjaVc9HAoAgDADB2HuJWLDx/2cKBITscW4L'.
'5byzMIWtZobNDZIZtrcCGZsRQ8GwvRSRNxIiMuysODKG3alikl'.
'ueOPlpKTLBaRmOZxQxaXlfb5ZWI9om4WntrXiDSJzp7SBkwMQa'.
'FEy0VR/NAB2kNuj7rgAAAABJRU5ErkJggg==' ;
 
//==========================================================
//sq_orange.png
//==========================================================
$this->imgdata[7][0]= 321 ;
$this->imgdata[7][1]=
'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAUV'.
'BMVEX/////8+n/8uf/8OP/59H/5Mv/zqH/zJ3/ypv/yJf/vYH/'.
'u33/uXn/n0n/nUX/m0H/lzn/ljf/lDP/kS3/kCv/iR//hxv/fg'.
'n/fAX/eQDYZgDW6ia5AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAL'.
'EgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEJIgbx+cAAAB2SURBVH'.
'jaVczRCoQwDETRbLAWLZSGUA35/w/dVI0283i4DODew3YESmWW'.
'kg5gWkoQAe6TleUQI/66Sy7i56+kLk7cht2N0+hcnJgQu0SqiC'.
'1SzSIbzWSi6gavqJ63wSduRi2f+kwyD5rEukwCdZ1kGAMGMfv9'.
'AbWuGMOr5COSAAAAAElFTkSuQmCC' ;
}
}
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph.php
New file
0,0 → 1,8033
<?php
//=======================================================================
// File: JPGRAPH.PHP
// Description: PHP4 Graph Plotting library. Base module.
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2001,2002,2003 Johan Persson
//========================================================================
 
//------------------------------------------------------------------------
// Directories for cache and font directory.
// Leave them undefined to use default values.
//
// Default values used if these defines are left commented out are:
//
// UNIX:
// CACHE_DIR = /tmp/jpgraph_cache/
// TTF_DIR = /usr/X11R6/lib/X11/fonts/truetype/
//
// WINDOWS:
// CACHE_DIR = $SERVER_TEMP/jpgraph_cache/
// TTF_DIR = $SERVER_SYSTEMROOT/fonts/
//
//
//------------------------------------------------------------------------
 
// The full absolute name of the directory to be used to store the
// cached image files. This directory will not be used if the USE_CACHE
// define (further down) is false. If you enable the cache please note that
// this directory MUST be readable and writable for the process running PHP.
// Must end with '/'
DEFINE("CACHE_DIR","/home/telabotap/www/tmp/jpgraph_cache/");
 
// Directory for jpGraph TTF fonts. Must end with '/'
// DEFINE("TTF_DIR","/usr/X11R6/lib/X11/fonts/truetype/");
 
 
//-------------------------------------------------------------------------
// Cache directory specification for use with CSIM graphs that are
// using the cache.
// The directory must be the filesysystem name as seen by PHP
// and the 'http' version must be the same directory but as
// seen by the HTTP server relative to the 'htdocs' ddirectory.
// If a relative path is specified it is taken to be relative from where
// the image script is executed.
// Note: The default setting is to create a subdirectory in the
// directory from where the image script is executed and store all files
// there. As ususal this directory must be writeable by the PHP process.
DEFINE("CSIMCACHE_DIR","csimcache/");
DEFINE("CSIMCACHE_HTTP_DIR","csimcache/");
 
//------------------------------------------------------------------------
// Various JpGraph Settings. Adjust accordingly to your
// preferences. Note that cache functionality is turned off by
// default (Enable by setting USE_CACHE to true)
//------------------------------------------------------------------------
 
// Deafult graphic format set to "auto" which will automatically
// choose the best available format in the order png,gif,jpg
// (The supported format depends on what your PHP installation supports)
DEFINE("DEFAULT_GFORMAT","auto");
 
// Should the image be a truecolor image?
// Note 1: Has only effect with GD 2.0.1 and above.
// Note 2: GD 2.0.1 + PHP 4.0.6 on Win32 crashes when trying to use
// trucolor. Truecolor support is to be considered alpha since GD 2.x
// is still not considered stable (especially on Win32).
// Note 3: MUST be enabled to get background images working with GD2
// Note 4: If enabled then truetype fonts will look very ugly with GD 2.0.1
// => You can't have both background images and truetype fonts in the same
// image until these bugs has been fixed in GD 2.01. There is a patch
// available for GD 2.0.1 though. See the README file.
DEFINE('USE_TRUECOLOR',true);
 
// Specify what version of the GD library is installed.
// If this is set to 'auto' the version will be automatically
// determined.
// However since determining the library takes ~1ms you can also
// manually specify the version if you know what version you have.
// This means that you should
// set this define to true if you have GD 2.x installed to save 1ms.
DEFINE("USE_LIBRARY_GD2",'auto');
 
// Should the cache be used at all? By setting this to false no
// files will be generated in the cache directory.
// The difference from READ_CACHE being that setting READ_CACHE to
// false will still create the image in the cache directory
// just not use it. By setting USE_CACHE=false no files will even
// be generated in the cache directory.
DEFINE("USE_CACHE",true);
 
// Should we try to find an image in the cache before generating it?
// Set this define to false to bypass the reading of the cache and always
// regenerate the image. Note that even if reading the cache is
// disabled the cached will still be updated with the newly generated
// image. Set also "USE_CACHE" below.
DEFINE("READ_CACHE",false);
 
// Determine if the error handler should be image based or purely
// text based. Image based makes it easier since the script will
// always return an image even in case of errors.
DEFINE("USE_IMAGE_ERROR_HANDLER",true);
 
// If the color palette is full should JpGraph try to allocate
// the closest match? If you plan on using background images or
// gradient fills it might be a good idea to enable this.
// If not you will otherwise get an error saying that the color palette is
// exhausted. The drawback of using approximations is that the colors
// might not be exactly what you specified.
// Note1: This does only apply to paletted images, not truecolor
// images since they don't have the limitations of maximum number
// of colors.
DEFINE("USE_APPROX_COLORS",true);
 
// Special unicode cyrillic language support
DEFINE("LANGUAGE_CYRILLIC",false);
 
// If you are setting this config to true the conversion
// will assume that the input text is windows 1251, if
// false it will assume koi8-r
DEFINE("CYRILLIC_FROM_WINDOWS",false);
 
// Should usage of deprecated functions and parameters give a fatal error?
// (Useful to check if code is future proof.)
DEFINE("ERR_DEPRECATED",true);
 
// Should the time taken to generate each picture be branded to the lower
// left in corner in each generated image? Useful for performace measurements
// generating graphs
DEFINE("BRAND_TIMING",false);
 
// What format should be used for the timing string?
DEFINE("BRAND_TIME_FORMAT","(%01.3fs)");
 
//------------------------------------------------------------------------
// The following constants should rarely have to be changed !
//------------------------------------------------------------------------
 
// What group should the cached file belong to
// (Set to "" will give the default group for the "PHP-user")
// Please note that the Apache user must be a member of the
// specified group since otherwise it is impossible for Apache
// to set the specified group.
DEFINE("CACHE_FILE_GROUP","wwwadmin");
 
// What permissions should the cached file have
// (Set to "" will give the default persmissions for the "PHP-user")
DEFINE("CACHE_FILE_MOD",0664);
 
// Decide if we should use the bresenham circle algorithm or the
// built in Arc(). Bresenham gives better visual apperance of circles
// but is more CPU intensive and slower then the built in Arc() function
// in GD. Turned off by default for speed
DEFINE("USE_BRESENHAM",false);
 
// Special file name to indicate that we only want to calc
// the image map in the call to Graph::Stroke() used
// internally from the GetHTMLCSIM() method.
DEFINE("_CSIM_SPECIALFILE","_csim_special_");
 
// HTTP GET argument that is used with image map
// to indicate to the script to just generate the image
// and not the full CSIM HTML page.
DEFINE("_CSIM_DISPLAY","_jpg_csimd");
 
// Special filename for Graph::Stroke(). If this filename is given
// then the image will NOT be streamed to browser of file. Instead the
// Stroke call will return the handler for the created GD image.
DEFINE("_IMG_HANDLER","__handle");
 
// DON'T SET THIS FLAG YORSELF THIS IS ONLY FOR INTERNAL TESTING
// PURPOSES. ENABLING THIS FLAG WILL MAKE SOME OF YOUR SCRIPT
// STOP WORKING
// Enable some extra debug information for CSIM etc to be shown.
DEFINE("JPG_DEBUG",false);
 
// Version info
DEFINE('JPG_VERSION','1.12');
 
//------------------------------------------------------------------------
// Automatic settings of path for cache and font directory
// if they have not been previously specified
//------------------------------------------------------------------------
if (!defined('CACHE_DIR')) {
if ( strstr( PHP_OS, 'WIN') ) {
if( empty($_SERVER['TEMP']) ) {
die('JpGraph Error: No path specified for CACHE_DIR. Please specify a path for that DEFINE in jpgraph.php');
}
else {
DEFINE('CACHE_DIR', $_SERVER['TEMP'] . '/');
}
} else {
DEFINE('CACHE_DIR','/tmp/jpgraph_cache/');
}
}
 
if (!defined('TTF_DIR')) {
if (strstr( PHP_OS, 'WIN') ) {
if( empty($_SERVER['SystemRoot']) ) {
die('JpGraph Error: No path specified for TTF_DIR. Please specify a path for that DEFINE in jpgraph.php');
}
else {
DEFINE('TTF_DIR', $_SERVER['SystemRoot'] . '/fonts/');
}
} else {
DEFINE('TTF_DIR','/usr/X11R6/lib/X11/fonts/truetype/');
}
}
 
//------------------------------------------------------------------
// Constants which are used as parameters for the method calls
//------------------------------------------------------------------
 
// TTF Font families
DEFINE("FF_COURIER",10);
DEFINE("FF_VERDANA",11);
DEFINE("FF_TIMES",12);
DEFINE("FF_COMIC",14);
DEFINE("FF_ARIAL",15);
DEFINE("FF_GEORGIA",16);
DEFINE("FF_TREBUCHE",17);
 
// Chinese font
DEFINE("FF_SIMSUN",18);
 
// Older deprecated fonts
DEFINE("FF_BOOK",91); // Deprecated fonts from 1.9
DEFINE("FF_HANDWRT",92); // Deprecated fonts from 1.9
 
// TTF Font styles
DEFINE("FS_NORMAL",9001);
DEFINE("FS_BOLD",9002);
DEFINE("FS_ITALIC",9003);
DEFINE("FS_BOLDIT",9004);
DEFINE("FS_BOLDITALIC",9004);
 
//Definitions for internal font, new style
DEFINE("FF_FONT0",1);
DEFINE("FF_FONT1",2);
DEFINE("FF_FONT2",4);
 
//Definitions for internal font, old style
// (Only defined here to be able to generate an error mesage
// when used)
DEFINE("FONT0",99); // Deprecated from 1.2
DEFINE("FONT1",98); // Deprecated from 1.2
DEFINE("FONT1_BOLD",97); // Deprecated from 1.2
DEFINE("FONT2",96); // Deprecated from 1.2
DEFINE("FONT2_BOLD",95); // Deprecated from 1.2
 
// Tick density
DEFINE("TICKD_DENSE",1);
DEFINE("TICKD_NORMAL",2);
DEFINE("TICKD_SPARSE",3);
DEFINE("TICKD_VERYSPARSE",4);
 
// Side for ticks and labels.
DEFINE("SIDE_LEFT",-1);
DEFINE("SIDE_RIGHT",1);
DEFINE("SIDE_DOWN",-1);
DEFINE("SIDE_BOTTOM",-1);
DEFINE("SIDE_UP",1);
DEFINE("SIDE_TOP",1);
 
// Legend type stacked vertical or horizontal
DEFINE("LEGEND_VERT",0);
DEFINE("LEGEND_HOR",1);
 
// Mark types for plot marks
DEFINE("MARK_SQUARE",1);
DEFINE("MARK_UTRIANGLE",2);
DEFINE("MARK_DTRIANGLE",3);
DEFINE("MARK_DIAMOND",4);
DEFINE("MARK_CIRCLE",5);
DEFINE("MARK_FILLEDCIRCLE",6);
DEFINE("MARK_CROSS",7);
DEFINE("MARK_STAR",8);
DEFINE("MARK_X",9);
DEFINE("MARK_LEFTTRIANGLE",10);
DEFINE("MARK_RIGHTTRIANGLE",11);
DEFINE("MARK_FLASH",12);
DEFINE("MARK_IMG",13);
 
// Builtin images
DEFINE("MARK_IMG_PUSHPIN",50);
DEFINE("MARK_IMG_SPUSHPIN",50);
DEFINE("MARK_IMG_LPUSHPIN",51);
DEFINE("MARK_IMG_DIAMOND",52);
DEFINE("MARK_IMG_SQUARE",53);
DEFINE("MARK_IMG_STAR",54);
DEFINE("MARK_IMG_BALL",55);
DEFINE("MARK_IMG_SBALL",55);
DEFINE("MARK_IMG_MBALL",56);
DEFINE("MARK_IMG_LBALL",57);
DEFINE("MARK_IMG_BEVEL",58);
 
// Styles for gradient color fill
DEFINE("GRAD_VER",1);
DEFINE("GRAD_VERT",1);
DEFINE("GRAD_HOR",2);
DEFINE("GRAD_MIDHOR",3);
DEFINE("GRAD_MIDVER",4);
DEFINE("GRAD_CENTER",5);
DEFINE("GRAD_WIDE_MIDVER",6);
DEFINE("GRAD_WIDE_MIDHOR",7);
DEFINE("GRAD_LEFT_REFLECTION",8);
DEFINE("GRAD_RIGHT_REFLECTION",9);
 
// Inline defines
DEFINE("INLINE_YES",1);
DEFINE("INLINE_NO",0);
 
// Format for background images
DEFINE("BGIMG_FILLPLOT",1);
DEFINE("BGIMG_FILLFRAME",2);
DEFINE("BGIMG_COPY",3);
DEFINE("BGIMG_CENTER",4);
 
// Depth of objects
DEFINE("DEPTH_BACK",0);
DEFINE("DEPTH_FRONT",1);
 
// Direction
DEFINE("VERTICAL",1);
DEFINE("HORIZONTAL",0);
 
// Constants for types of static bands in plot area
DEFINE("BAND_RDIAG",1); // Right diagonal lines
DEFINE("BAND_LDIAG",2); // Left diagonal lines
DEFINE("BAND_SOLID",3); // Solid one color
DEFINE("BAND_VLINE",4); // Vertical lines
DEFINE("BAND_HLINE",5); // Horizontal lines
DEFINE("BAND_3DPLANE",6); // "3D" Plane
DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses
 
// Axis styles for scientific style axis
DEFINE('AXSTYLE_SIMPLE',1);
DEFINE('AXSTYLE_BOXIN',2);
DEFINE('AXSTYLE_BOXOUT',3);
DEFINE('AXSTYLE_YBOXIN',4);
DEFINE('AXSTYLE_YBOXOUT',5);
 
// Style for title backgrounds
DEFINE('TITLEBKG_STYLE1',1);
DEFINE('TITLEBKG_STYLE2',2);
DEFINE('TITLEBKG_STYLE3',3);
DEFINE('TITLEBKG_FRAME_NONE',0);
DEFINE('TITLEBKG_FRAME_FULL',1);
DEFINE('TITLEBKG_FRAME_BOTTOM',2);
DEFINE('TITLEBKG_FRAME_BEVEL',3);
DEFINE('TITLEBKG_FILLSTYLE_HSTRIPED',1);
DEFINE('TITLEBKG_FILLSTYLE_VSTRIPED',2);
DEFINE('TITLEBKG_FILLSTYLE_SOLID',3);
 
// Width of tab titles
DEFINE('TABTITLE_WIDTHFIT',0);
DEFINE('TABTITLE_WIDTHFULL',-1);
 
//
// Get hold of gradient class (In Version 2.x)
// A client of the library has to manually include this
//
include "jpgraph_gradient.php";
 
//
// First of all set up a default error handler
//
 
//=============================================================
// The default trivial text error handler.
//=============================================================
class JpGraphErrObject {
function JpGraphErrObject() {
// Empty. Reserved for future use
}
 
// If aHalt is true then execution can't continue. Typical used for
// fatal errors
function Raise($aMsg,$aHalt=true) {
$aMsg = "<b>JpGraph Error:</b> ".$aMsg;
if( $aHalt )
die($aMsg);
else
echo $aMsg."<p>";
}
}
 
//==============================================================
// An image based error handler
//==============================================================
class JpGraphErrObjectImg {
 
function Raise($aMsg,$aHalt=true) {
if( headers_sent() ) {
// Special case for headers already sent error. Dont
// return an image since it can't be displayed
die("<b>JpGraph Error:</b> ".$aMsg);
}
 
// Create an image that contains the error text.
$w=450; $h=110;
$img = new Image($w,$h);
$img->SetColor("darkred");
$img->Rectangle(0,0,$w-1,$h-1);
$img->SetFont(FF_FONT1,FS_BOLD);
$img->StrokeText(10,20,"JpGraph Error:");
$img->SetColor("black");
$img->SetFont(FF_FONT1,FS_NORMAL);
$txt = new Text(wordwrap($aMsg,70),10,20);
$txt->Align("left","top");
$txt->Stroke($img);
$img->Headers();
$img->Stream();
die();
}
}
 
//
// A wrapper class that is used to access the specified error object
// (to hide the global error parameter and avoid having a GLOBAL directive
// in all methods.
//
class JpGraphError {
function Install($aErrObject) {
GLOBAL $__jpg_err;
$__jpg_err = $aErrObject;
}
function Raise($aMsg,$aHalt=true){
GLOBAL $__jpg_err;
$tmp = new $__jpg_err;
$tmp->Raise($aMsg,$aHalt);
}
}
 
//
// ... and install the default error handler
//
if( USE_IMAGE_ERROR_HANDLER ) {
JpGraphError::Install("JpGraphErrObjectImg");
}
else {
JpGraphError::Install("JpGraphErrObject");
}
 
 
//
//Check if there were any warnings, perhaps some wrong includes by the
//user
//
if( isset($GLOBALS['php_errormsg']) ) {
JpGraphError::Raise("<b>General PHP error:</b><br>".$GLOBALS['php_errormsg']);
}
 
 
//
// Routine to determine if GD1 or GD2 is installed
//
function CheckGDVersion() {
ob_start();
phpinfo(8); // Just get the modules loaded
$a = ob_get_contents();
ob_end_clean();
if( preg_match('/.*GD Version.*(1\.).*/',$a,$m) ) {
$r=1;$v=$m[1];
}
elseif( preg_match('/.*GD Version.*(2\.).*/',$a,$m) ) {
$r=2;$v=$m[1];
}
else {
$r=0;$v=$m[1];
}
return $r;
}
 
//
// Check what version of the GD library is installed.
//
if( USE_LIBRARY_GD2 === 'auto' ) {
$gdversion = CheckGDVersion();
if( $gdversion == 2 ) {
$GLOBALS['gd2'] = true;
$GLOBALS['copyfunc'] = 'imagecopyresampled';
}
elseif( $gdversion == 1 ) {
$GLOBALS['gd2'] = false;
$GLOBALS['copyfunc'] = 'imagecopyresized';
}
else {
JpGraphError::Raise(" Your PHP installation does not seem to
have the required GD library.
Please see the PHP documentation on how to install and enable the GD library.");
}
}
else {
$GLOBALS['gd2'] = USE_LIBRARY_GD2;
$GLOBALS['copyfunc'] = USE_LIBRARY_GD2 ? 'imagecopyresampled' : 'imagecopyresized';
}
 
// Usefull mathematical function
function sign($a) {return $a >= 0 ? 1 : -1;}
 
// Utility function to generate an image name based on the filename we
// are running from and assuming we use auto detection of graphic format
// (top level), i.e it is safe to call this function
// from a script that uses JpGraph
function GenImgName() {
global $HTTP_SERVER_VARS;
$supported = imagetypes();
if( $supported & IMG_PNG )
$img_format="png";
elseif( $supported & IMG_GIF )
$img_format="gif";
elseif( $supported & IMG_JPG )
$img_format="jpeg";
if( !isset($HTTP_SERVER_VARS['PHP_SELF']) )
JpGraphError::Raise(" Can't access PHP_SELF, PHP global variable. You can't run PHP from command line
if you want to use the 'auto' naming of cache or image files.");
$fname=basename($HTTP_SERVER_VARS['PHP_SELF']);
// Replace the ".php" extension with the image format extension
return substr($fname,0,strlen($fname)-4).".".$img_format;
}
 
class LanguageConv {
var $g2312 = null ;
 
function Convert($aTxt,$aFF) {
if( LANGUAGE_CYRILLIC ) {
if( CYRILLIC_FROM_WINDOWS ) {
$aTxt = convert_cyr_string($aTxt, "w", "k");
}
$isostring = convert_cyr_string($aTxt, "k", "i");
$unistring = LanguageConv::iso2uni($isostring);
return $unistring;
}
elseif( $aFF === FF_SIMSUN ) {
// Do Chinese conversion
if( $this->g2312 == null ) {
include_once 'jpgraph_gb2312.php' ;
$this->g2312 = new GB2312toUTF8();
}
return $this->g2312->gb2utf8($aTxt);
}
else
return $aTxt;
}
 
// Translate iso encoding to unicode
function iso2uni ($isoline){
for ($i=0; $i < strlen($isoline); $i++){
$thischar=substr($isoline,$i,1);
$charcode=ord($thischar);
$uniline.=($charcode>175) ? "&#" . (1040+($charcode-176)). ";" : $thischar;
}
return $uniline;
}
}
 
//===================================================
// CLASS JpgTimer
// Description: General timing utility class to handle
// timne measurement of generating graphs. Multiple
// timers can be started by pushing new on a stack.
//===================================================
class JpgTimer {
var $start;
var $idx;
//---------------
// CONSTRUCTOR
function JpgTimer() {
$this->idx=0;
}
 
//---------------
// PUBLIC METHODS
 
// Push a new timer start on stack
function Push() {
list($ms,$s)=explode(" ",microtime());
$this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
}
 
// Pop the latest timer start and return the diff with the
// current time
function Pop() {
assert($this->idx>0);
list($ms,$s)=explode(" ",microtime());
$etime=floor($ms*1000) + (1000*$s);
$this->idx--;
return $etime-$this->start[$this->idx];
}
} // Class
 
$gJpgBrandTiming = BRAND_TIMING;
//===================================================
// CLASS DateLocale
// Description: Hold localized text used in dates
// ToDOo: Rewrite this to use the real local locale
// instead.
//===================================================
class DateLocale {
var $iLocale = 'C'; // environmental locale be used by default
 
var $iDayAbb = null;
var $iShortDay = null;
var $iShortMonth = null;
var $iMonthName = null;
 
//---------------
// CONSTRUCTOR
function DateLocale() {
settype($this->iDayAbb, 'array');
settype($this->iShortDay, 'array');
settype($this->iShortMonth, 'array');
settype($this->iMonthName, 'array');
 
 
$this->Set('C');
}
 
//---------------
// PUBLIC METHODS
function Set($aLocale) {
if ( in_array($aLocale, array_keys($this->iDayAbb)) ){
$this->iLocale = $aLocale;
return TRUE; // already cached nothing else to do!
}
 
$pLocale = setlocale(LC_TIME, 0); // get current locale for LC_TIME
$res = setlocale(LC_TIME, $aLocale);
if ( ! $res ){
JpGraphError::Raise("You are trying to use the locale ($aLocale) which your PHP installation does not support. Hint: Use '' to indicate the default locale for this geographic region.");
return FALSE;
}
$this->iLocale = $aLocale;
 
for ( $i = 0, $ofs = 0 - strftime('%w'); $i < 7; $i++, $ofs++ ){
$day = strftime('%a', strtotime("$ofs day"));
$day{0} = strtoupper($day{0});
$this->iDayAbb[$aLocale][]= $day{0};
$this->iShortDay[$aLocale][]= $day;
}
 
for($i=1; $i<=12; ++$i) {
list($short ,$full) = explode('|', strftime("%b|%B",strtotime("2001-$i-01")));
$this->iShortMonth[$aLocale][] = ucfirst($short);
$this->iMonthName [$aLocale][] = ucfirst($full);
}
setlocale(LC_TIME, $pLocale);
 
return TRUE;
}
 
 
function GetDayAbb() {
return $this->iDayAbb[$this->iLocale];
}
function GetShortDay() {
return $this->iShortDay[$this->iLocale];
}
 
function GetShortMonth() {
return $this->iShortMonth[$this->iLocale];
}
function GetShortMonthName($aNbr) {
return $this->iShortMonth[$this->iLocale][$aNbr];
}
 
function GetLongMonthName($aNbr) {
return $this->iMonthName[$this->iLocale][$aNbr];
}
 
function GetMonth() {
return $this->iMonthName[$this->iLocale];
}
}
 
$gDateLocale = new DateLocale();
$gJpgDateLocale = new DateLocale();
 
 
//===================================================
// CLASS FuncGenerator
// Description: Utility class to help generate data for function plots.
// The class supports both parametric and regular functions.
//===================================================
class FuncGenerator {
var $iFunc='',$iXFunc='',$iMin,$iMax,$iStepSize;
function FuncGenerator($aFunc,$aXFunc='') {
$this->iFunc = $aFunc;
$this->iXFunc = $aXFunc;
}
function E($aXMin,$aXMax,$aSteps=50) {
$this->iMin = $aXMin;
$this->iMax = $aXMax;
$this->iStepSize = ($aXMax-$aXMin)/$aSteps;
 
if( $this->iXFunc != '' )
$t = 'for($i='.$aXMin.'; $i<='.$aXMax.'; $i += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]='.$this->iXFunc.';}';
elseif( $this->iFunc != '' )
$t = 'for($x='.$aXMin.'; $x<='.$aXMax.'; $x += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]=$x;} $x='.$aXMax.';$ya[]='.$this->iFunc.';$xa[]=$x;';
else
JpGraphError::Raise('FuncGenerator : No function specified. ');
@eval($t);
// If there is an error in the function specifcation this is the only
// way we can discover that.
if( empty($xa) || empty($ya) )
JpGraphError::Raise('FuncGenerator : Syntax error in function specification ');
return array($xa,$ya);
}
}
 
 
//=======================================================
// CLASS Footer
// Description: Encapsulates the footer line in the Graph
//
//=======================================================
class Footer {
var $left,$center,$right;
var $iLeftMargin = 3;
var $iRightMargin = 3;
var $iBottomMargin = 3;
 
function Footer() {
$this->left = new Text();
$this->left->ParagraphAlign('left');
$this->center = new Text();
$this->center->ParagraphAlign('center');
$this->right = new Text();
$this->right->ParagraphAlign('right');
}
 
function Stroke($aImg) {
$y = $aImg->height - $this->iBottomMargin;
$x = $this->iLeftMargin;
$this->left->Align('left','bottom');
$this->left->Stroke($aImg,$x,$y);
 
$x = ($aImg->width - $this->iLeftMargin - $this->iRightMargin)/2;
$this->center->Align('center','bottom');
$this->center->Stroke($aImg,$x,$y);
 
$x = $aImg->width - $this->iRightMargin;
$this->right->Align('right','bottom');
$this->right->Stroke($aImg,$x,$y);
}
}
 
DEFINE('BGRAD_FRAME',1);
DEFINE('BGRAD_MARGIN',2);
DEFINE('BGRAD_PLOT',3);
 
 
 
//===================================================
// CLASS Graph
// Description: Main class to handle graphs
//===================================================
class Graph {
var $cache=null; // Cache object (singleton)
var $img=null; // Img object (singleton)
var $plots=array(); // Array of all plot object in the graph (for Y 1 axis)
var $y2plots=array();// Array of all plot object in the graph (for Y 2 axis)
var $xscale=null; // X Scale object (could be instance of LinearScale or LogScale
var $yscale=null,$y2scale=null;
var $cache_name; // File name to be used for the current graph in the cache directory
var $xgrid=null; // X Grid object (linear or logarithmic)
var $ygrid=null,$y2grid=null; //dito for Y
var $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph
var $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area
var $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph
var $xaxis=null; // X-axis (instane of Axis class)
var $yaxis=null, $y2axis=null; // Y axis (instance of Axis class)
var $margin_color=array(200,200,200); // Margin color of graph
var $plotarea_color=array(255,255,255); // Plot area color
var $title,$subtitle,$subsubtitle; // Title and subtitle(s) text object
var $axtype="linlin"; // Type of axis
var $xtick_factor; // Factot to determine the maximum number of ticks depending on the plot with
var $texts=null; // Text object to ge shown in the graph
var $lines=null;
var $bands=null;
var $text_scale_off=0; // Text scale offset in world coordinates
var $background_image="",$background_image_type=-1,$background_image_format="png";
var $background_image_bright=0,$background_image_contr=0,$background_image_sat=0;
var $image_bright=0, $image_contr=0, $image_sat=0;
var $inline;
var $showcsim=0,$csimcolor="red"; //debug stuff, draw the csim boundaris on the image if <>0
var $grid_depth=DEPTH_BACK; // Draw grid under all plots as default
var $iAxisStyle = AXSTYLE_SIMPLE;
var $iCSIMdisplay=false,$iHasStroked = false;
var $footer;
var $csimcachename = '', $csimcachetimeout = 0;
var $iDoClipping = false;
var $y2orderback=true;
var $tabtitle;
var $bkg_gradtype=-1,$bkg_gradstyle=BGRAD_MARGIN;
var $bkg_gradfrom='navy', $bkg_gradto='silver';
var $titlebackground = false;
var $titlebackground_color = 'lightblue',
$titlebackground_style = 1,
$titlebackground_framecolor = 'blue',
$titlebackground_framestyle = 2,
$titlebackground_frameweight = 1,
$titlebackground_bevelheight = 3 ;
var $titlebkg_fillstyle=TITLEBKG_FILLSTYLE_SOLID;
var $titlebkg_scolor1='black',$titlebkg_scolor2='white';
var $framebevel = false, $framebeveldepth = 2 ;
var $framebevelborder = false, $framebevelbordercolor='black';
var $framebevelcolor1='white@0.4', $framebevelcolor2='black@0.4';
 
//---------------
// CONSTRUCTOR
 
// aWIdth Width in pixels of image
// aHeight Height in pixels of image
// aCachedName Name for image file in cache directory
// aTimeOut Timeout in minutes for image in cache
// aInline If true the image is streamed back in the call to Stroke()
// If false the image is just created in the cache
function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
GLOBAL $gJpgBrandTiming;
// If timing is used create a new timing object
if( $gJpgBrandTiming ) {
global $tim;
$tim = new JpgTimer();
$tim->Push();
}
// Automatically generate the image file name based on the name of the script that
// generates the graph
if( $aCachedName=="auto" )
$aCachedName=GenImgName();
// Should the image be streamed back to the browser or only to the cache?
$this->inline=$aInline;
$this->img = new RotImage($aWidth,$aHeight);
 
$this->cache = new ImgStreamCache($this->img);
$this->cache->SetTimeOut($aTimeOut);
 
$this->title = new Text();
$this->title->ParagraphAlign('center');
$this->title->SetFont(FF_FONT2,FS_BOLD);
$this->title->SetMargin(3);
 
$this->subtitle = new Text();
$this->subtitle->ParagraphAlign('center');
 
$this->subsubtitle = new Text();
$this->subsubtitle->ParagraphAlign('center');
 
$this->legend = new Legend();
$this->footer = new Footer();
 
// If the cached version exist just read it directly from the
// cache, stream it back to browser and exit
if( $aCachedName!="" && READ_CACHE && $aInline )
if( $this->cache->GetAndStream($aCachedName) ) {
exit();
}
$this->cache_name = $aCachedName;
$this->SetTickDensity(); // Normal density
 
$this->tabtitle = new GraphTabTitle();
}
//---------------
// PUBLIC METHODS
 
// Should the grid be in front or back of the plot?
function SetGridDepth($aDepth) {
$this->grid_depth=$aDepth;
}
// Specify graph angle 0-360 degrees.
function SetAngle($aAngle) {
$this->img->SetAngle($aAngle);
}
 
function SetAlphaBlending($aFlg=true) {
$this->img->SetAlphaBlending($aFlg);
}
 
// Shortcut to image margin
function SetMargin($lm,$rm,$tm,$bm) {
$this->img->SetMargin($lm,$rm,$tm,$bm);
}
 
function SetY2OrderBack($aBack=true) {
$this->y2orderback = $aBack;
}
 
// Rotate the graph 90 degrees and set the margin
// when we have done a 90 degree rotation
function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) {
$lm = $lm ==0 ? floor(0.2 * $this->img->width) : $lm ;
$rm = $rm ==0 ? floor(0.1 * $this->img->width) : $rm ;
$tm = $tm ==0 ? floor(0.2 * $this->img->height) : $tm ;
$bm = $bm ==0 ? floor(0.1 * $this->img->height) : $bm ;
 
$adj = ($this->img->height - $this->img->width)/2;
$this->img->SetMargin($tm-$adj,$bm-$adj,$rm+$adj,$lm+$adj);
$this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2));
$this->SetAngle(90);
$this->xaxis->SetLabelAlign('right','center');
$this->yaxis->SetLabelAlign('center','bottom');
}
function SetClipping($aFlg=true) {
$this->iDoClipping = $aFlg ;
}
 
// Add a plot object to the graph
function Add(&$aPlot) {
if( $aPlot == null )
JpGraphError::Raise("<b></b> Graph::Add() You tried to add a null plot to the graph.");
if( is_array($aPlot) && count($aPlot) > 0 )
$cl = get_class($aPlot[0]);
else
$cl = get_class($aPlot);
 
if( $cl == 'text' )
$this->AddText($aPlot);
elseif( $cl == 'plotline' )
$this->AddLine($aPlot);
elseif( $cl == 'plotband' )
$this->AddBand($aPlot);
else
$this->plots[] = &$aPlot;
}
 
// Add plot to second Y-scale
function AddY2(&$aPlot) {
if( $aPlot == null )
JpGraphError::Raise("<b></b> Graph::AddY2() You tried to add a null plot to the graph.");
$this->y2plots[] = &$aPlot;
}
// Add text object to the graph
function AddText(&$aTxt) {
if( $aTxt == null )
JpGraphError::Raise("<b></b> Graph::AddText() You tried to add a null text to the graph.");
if( is_array($aTxt) ) {
for($i=0; $i < count($aTxt); ++$i )
$this->texts[]=&$aTxt[$i];
}
else
$this->texts[] = &$aTxt;
}
// Add a line object (class PlotLine) to the graph
function AddLine(&$aLine) {
if( $aLine == null )
JpGraphError::Raise("<b></b> Graph::AddLine() You tried to add a null line to the graph.");
if( is_array($aLine) ) {
for($i=0; $i<count($aLine); ++$i )
$this->lines[]=&$aLine[$i];
}
else
$this->lines[] = &$aLine;
}
 
// Add vertical or horizontal band
function AddBand(&$aBand) {
if( $aBand == null )
JpGraphError::Raise(" Graph::AddBand() You tried to add a null band to the graph.");
if( is_array($aBand) ) {
for($i=0; $i<count($aBand); ++$i )
$this->bands[] = &$aBand[$i];
}
else
$this->bands[] = &$aBand;
}
 
function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=GRAD_HOR,$aStyle=BGRAD_FRAME) {
$this->bkg_gradtype=$aGradType;
$this->bkg_gradstyle=$aStyle;
$this->bkg_gradfrom = $aFrom;
$this->bkg_gradto = $aTo;
}
// Specify a background image
function SetBackgroundImage($aFileName,$aBgType=BGIMG_FILLPLOT,$aImgFormat="auto") {
 
if( $GLOBALS['gd2'] && !USE_TRUECOLOR ) {
JpGraphError::Raise("You are using GD 2.x and are trying to use a background images on a non truecolor image. To use background images with GD 2.x you <b>must</b> enable truecolor by setting the USE_TRUECOLOR constant to TRUE. Due to a bug in GD 2.0.1 using any truetype fonts with truecolor images will result in very poor quality fonts.");
}
 
// Get extension to determine image type
if( $aImgFormat == "auto" ) {
$e = explode('.',$aFileName);
if( !$e ) {
JpGraphError::Raise('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type');
}
 
$valid_formats = array('png', 'jpg', 'gif');
$aImgFormat = strtolower($e[count($e)-1]);
if ($aImgFormat == 'jpeg') {
$aImgFormat = 'jpg';
}
elseif (!in_array($aImgFormat, $valid_formats) ) {
JpGraphError::Raise('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
}
}
 
$this->background_image = $aFileName;
$this->background_image_type=$aBgType;
$this->background_image_format=$aImgFormat;
}
// Adjust brightness and constrast for background image
function AdjBackgroundImage($aBright,$aContr=0,$aSat=0) {
$this->background_image_bright=$aBright;
$this->background_image_contr=$aContr;
$this->background_image_sat=$aSat;
}
// Adjust brightness and constrast for image
function AdjImage($aBright,$aContr=0,$aSat=0) {
$this->image_bright=$aBright;
$this->image_contr=$aContr;
$this->image_sat=$aSat;
}
 
// Specify axis style (boxed or single)
function SetAxisStyle($aStyle) {
$this->iAxisStyle = $aStyle ;
}
// Set a frame around the plot area
function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) {
$this->boxed = $aDrawPlotFrame;
$this->box_weight = $aPlotFrameWeight;
$this->box_color = $aPlotFrameColor;
}
// Specify color for the plotarea (not the margins)
function SetColor($aColor) {
$this->plotarea_color=$aColor;
}
// Specify color for the margins (all areas outside the plotarea)
function SetMarginColor($aColor) {
$this->margin_color=$aColor;
}
// Set a frame around the entire image
function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) {
$this->doframe = $aDrawImgFrame;
$this->frame_color = $aImgFrameColor;
$this->frame_weight = $aImgFrameWeight;
}
 
function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor='black',$aColor1='white@0.4',$aColor2='darkgray@0.4',$aFlg=true) {
$this->framebevel = $aFlg ;
$this->framebeveldepth = $aDepth ;
$this->framebevelborder = $aBorder ;
$this->framebevelbordercolor = $aBorderColor ;
$this->framebevelcolor1 = $aColor1 ;
$this->framebevelcolor2 = $aColor2 ;
 
$this->doshadow = false ;
}
 
// Set the shadow around the whole image
function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) {
$this->doshadow = $aShowShadow;
$this->shadow_color = $aShadowColor;
$this->shadow_width = $aShadowWidth;
$this->footer->iBottomMargin += $aShadowWidth;
$this->footer->iRightMargin += $aShadowWidth;
}
 
// Specify x,y scale. Note that if you manually specify the scale
// you must also specify the tick distance with a call to Ticks::Set()
function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
$this->axtype = $aAxisType;
 
if( $aYMax < $aYMin || $aXMax < $aXMin )
JpGraphError::Raise('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
 
$yt=substr($aAxisType,-3,3);
if( $yt=="lin" )
$this->yscale = new LinearScale($aYMin,$aYMax);
elseif( $yt == "int" ) {
$this->yscale = new LinearScale($aYMin,$aYMax);
$this->yscale->SetIntScale();
}
elseif( $yt=="log" )
$this->yscale = new LogScale($aYMin,$aYMax);
else
JpGraphError::Raise("Unknown scale specification for Y-scale. ($aAxisType)");
$xt=substr($aAxisType,0,3);
if( $xt == "lin" || $xt == "tex" ) {
$this->xscale = new LinearScale($aXMin,$aXMax,"x");
$this->xscale->textscale = ($xt == "tex");
}
elseif( $xt == "int" ) {
$this->xscale = new LinearScale($aXMin,$aXMax,"x");
$this->xscale->SetIntScale();
}
elseif( $xt == "log" )
$this->xscale = new LogScale($aXMin,$aXMax,"x");
else
JpGraphError::Raise(" Unknown scale specification for X-scale. ($aAxisType)");
 
$this->xscale->Init($this->img);
$this->yscale->Init($this->img);
$this->xaxis = new Axis($this->img,$this->xscale);
$this->yaxis = new Axis($this->img,$this->yscale);
$this->xgrid = new Grid($this->xaxis);
$this->ygrid = new Grid($this->yaxis);
$this->ygrid->Show();
}
// Specify secondary Y scale
function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) {
if( $aAxisType=="lin" )
$this->y2scale = new LinearScale($aY2Min,$aY2Max);
elseif( $aAxisType == "int" ) {
$this->y2scale = new LinearScale($aY2Min,$aY2Max);
$this->y2scale->SetIntScale();
}
elseif( $aAxisType=="log" ) {
$this->y2scale = new LogScale($aY2Min,$aY2Max);
}
else JpGraphError::Raise("JpGraph: Unsupported Y2 axis type: $axtype<br>");
$this->y2scale->Init($this->img);
$this->y2axis = new Axis($this->img,$this->y2scale);
$this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
$this->y2axis->SetLabelSide(SIDE_RIGHT);
// Deafult position is the max x-value
$this->y2grid = new Grid($this->y2axis);
}
// Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
// The dividing factor have been determined heuristically according to my aesthetic
// sense (or lack off) y.m.m.v !
function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) {
$this->xtick_factor=30;
$this->ytick_factor=25;
switch( $aYDensity ) {
case TICKD_DENSE:
$this->ytick_factor=12;
break;
case TICKD_NORMAL:
$this->ytick_factor=25;
break;
case TICKD_SPARSE:
$this->ytick_factor=40;
break;
case TICKD_VERYSPARSE:
$this->ytick_factor=100;
break;
default:
JpGraphError::Raise("JpGraph: Unsupported Tick density: $densy");
}
switch( $aXDensity ) {
case TICKD_DENSE:
$this->xtick_factor=15;
break;
case TICKD_NORMAL:
$this->xtick_factor=30;
break;
case TICKD_SPARSE:
$this->xtick_factor=45;
break;
case TICKD_VERYSPARSE:
$this->xtick_factor=60;
break;
default:
JpGraphError::Raise("JpGraph: Unsupported Tick density: $densx");
}
}
 
// Get a string of all image map areas
function GetCSIMareas() {
if( !$this->iHasStroked )
$this->Stroke(_CSIM_SPECIALFILE);
$csim=$this->legend->GetCSIMAreas();
 
$n = count($this->plots);
for( $i=0; $i<$n; ++$i )
$csim .= $this->plots[$i]->GetCSIMareas();
 
$n = count($this->y2plots);
for( $i=0; $i<$n; ++$i )
$csim .= $this->y2plots[$i]->GetCSIMareas();
 
return $csim;
}
// Get a complete <MAP>..</MAP> tag for the final image map
function GetHTMLImageMap($aMapName) {
$im = "<MAP NAME=\"$aMapName\">\n";
$im .= $this->GetCSIMareas();
$im .= "</MAP>";
return $im;
}
 
function CheckCSIMCache($aCacheName,$aTimeOut=60) {
global $HTTP_SERVER_VARS;
 
if( $aCacheName=='auto' )
$aCacheName=basename($HTTP_SERVER_VARS['PHP_SELF']);
 
$this->csimcachename = CSIMCACHE_DIR.$aCacheName;
$this->csimcachetimeout = $aTimeOut;
 
// First determine if we need to check for a cached version
// This differs from the standard cache in the sense that the
// image and CSIM map HTML file is written relative to the directory
// the script executes in and not the specified cache directory.
// The reason for this is that the cache directory is not necessarily
// accessible from the HTTP server.
if( $this->csimcachename != '' ) {
$dir = dirname($this->csimcachename);
$base = basename($this->csimcachename);
$base = strtok($base,'.');
$suffix = strtok('.');
$basecsim = $dir.'/'.$base.'_csim_.html';
$baseimg = $dir.'/'.$base.'.'.$this->img->img_format;
 
$timedout=false;
// Does it exist at all ?
if( file_exists($basecsim) && file_exists($baseimg) ) {
// Check that it hasn't timed out
$diff=time()-filemtime($basecsim);
if( $this->csimcachetimeout>0 && ($diff > $this->csimcachetimeout*60) ) {
$timedout=true;
@unlink($basecsim);
@unlink($baseimg);
}
else {
if ($fh = @fopen($basecsim, "r")) {
fpassthru($fh);
exit();
}
else
JpGraphError::Raise(" Can't open cached CSIM \"$basecsim\" for reading.");
}
}
}
return false;
}
 
function StrokeCSIM($aScriptName='',$aCSIMName='',$aBorder=0) {
GLOBAL $HTTP_GET_VARS;
 
if( $aCSIMName=='' ) {
// create a random map name
srand ((double) microtime() * 1000000);
$r = rand(0,100000);
$aCSIMName='__mapname'.$r.'__';
}
if( empty($HTTP_GET_VARS[_CSIM_DISPLAY]) ) {
// First determine if we need to check for a cached version
// This differs from the standard cache in the sense that the
// image and CSIM map HTML file is written relative to the directory
// the script executes in and not the specified cache directory.
// The reason for this is that the cache directory is not necessarily
// accessible from the HTTP server.
if( $this->csimcachename != '' ) {
$dir = dirname($this->csimcachename);
$base = basename($this->csimcachename);
$base = strtok($base,'.');
$suffix = strtok('.');
$basecsim = $dir.'/'.$base.'_csim_.html';
$baseimg = $base.'.'.$this->img->img_format;
 
// Check that apache can write to directory specified
 
if( file_exists($dir) && !is_writeable($dir) ) {
JpgraphError::Raise('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
}
// Make sure directory exists
$this->cache->MakeDirs($dir);
 
// Write the image file
$this->Stroke(CSIMCACHE_DIR.$baseimg);
 
// Construct wrapper HTML and write to file and send it back to browser
$htmlwrap = $this->GetHTMLImageMap($aCSIMName)."\n".
'<img src="'.CSIMCACHE_HTTP_DIR.$baseimg.'" ISMAP USEMAP="#'.$aCSIMName.'" border='.$aBorder.'>'."\n";
if($fh = @fopen($basecsim,'w') ) {
fwrite($fh,$htmlwrap);
fclose($fh);
echo $htmlwrap;
}
else
JpGraphError::Raise(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
}
else {
 
if( $aScriptName=='' ) {
JpGraphError::Raise('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
exit();
}
 
// Construct the HTML wrapper page
// Get all user defined URL arguments
reset($HTTP_GET_VARS);
// This is a JPGRAPH internal defined that prevents
// us from recursively coming here again
$urlarg='?'._CSIM_DISPLAY.'=1';
 
while( list($key,$value) = each($HTTP_GET_VARS) ) {
if( is_array($value) ) {
$n = count($value);
for( $i=0; $i < $n; ++$i ) {
$urlarg .= '&'.$key.'%5B%5D='.urlencode($value[$i]);
}
}
else {
$urlarg .= '&'.$key.'='.urlencode($value);
}
}
echo $this->GetHTMLImageMap($aCSIMName);
 
echo "<img src='".$aScriptName.$urlarg."' ISMAP USEMAP='#".$aCSIMName."' border=$aBorder>";
}
}
else {
$this->Stroke();
}
}
 
function GetTextsYMinMax() {
$n = count($this->texts);
$min=null;
$max=null;
for( $i=0; $i < $n; ++$i ) {
if( $this->texts[$i]->iScalePosY !== null &&
$this->texts[$i]->iScalePosX !== null ) {
if( $min === null ) {
$min = $max = $this->texts[$i]->iScalePosY ;
}
else {
$min = min($min,$this->texts[$i]->iScalePosY);
$max = max($max,$this->texts[$i]->iScalePosY);
}
}
}
if( $min !== null ) {
return array($min,$max);
}
else
return null;
}
 
function GetTextsXMinMax() {
$n = count($this->texts);
$min=null;
$max=null;
for( $i=0; $i < $n; ++$i ) {
if( $this->texts[$i]->iScalePosY !== null &&
$this->texts[$i]->iScalePosX !== null ) {
if( $min === null ) {
$min = $max = $this->texts[$i]->iScalePosX ;
}
else {
$min = min($min,$this->texts[$i]->iScalePosX);
$max = max($max,$this->texts[$i]->iScalePosX);
}
}
}
if( $min !== null ) {
return array($min,$max);
}
else
return null;
}
 
 
 
function GetXMinMax() {
list($min,$ymin) = $this->plots[0]->Min();
list($max,$ymax) = $this->plots[0]->Max();
foreach( $this->plots as $p ) {
list($xmin,$ymin) = $p->Min();
list($xmax,$ymax) = $p->Max();
$min = Min($xmin,$min);
$max = Max($xmax,$max);
}
if( $this->y2axis != null ) {
foreach( $this->y2plots as $p ) {
list($xmin,$ymin) = $p->Min();
list($xmax,$ymax) = $p->Max();
$min = Min($xmin,$min);
$max = Max($xmax,$max);
}
}
return array($min,$max);
}
 
function AdjustMarginsForTitles() {
$totrequired =
($this->title->t != '' ?
$this->title->GetTextHeight($this->img) + $this->title->margin + 5 : 0 ) +
($this->subtitle->t != '' ?
$this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 : 0 ) +
($this->subsubtitle->t != '' ?
$this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 : 0 ) ;
 
$btotrequired = 0;
if( !$this->xaxis->hide && !$this->xaxis->hide_labels ) {
// Minimum bottom margin
if( $this->xaxis->title->t != '' ) {
if( $this->img->a == 90 )
$btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 5 ;
else
$btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 5 ;
}
else
$btotrequired = 0;
if( $this->img->a == 90 ) {
$this->img->SetFont($this->yaxis->font_family,$this->yaxis->font_style,
$this->yaxis->font_size);
$lh = $this->img->GetTextHeight('Mg',$this->yaxis->label_angle);
}
else {
$this->img->SetFont($this->xaxis->font_family,$this->xaxis->font_style,
$this->xaxis->font_size);
$lh = $this->img->GetTextHeight('Mg',$this->xaxis->label_angle);
}
$btotrequired += $lh + 5;
}
 
if( $this->img->a == 90 ) {
// DO Nothing. It gets too messy to do this properly for 90 deg...
}
else{
if( $this->img->top_margin < $totrequired ) {
$this->SetMargin($this->img->left_margin,$this->img->right_margin,
$totrequired,$this->img->bottom_margin);
}
if( $this->img->bottom_margin < $btotrequired ) {
$this->SetMargin($this->img->left_margin,$this->img->right_margin,
$this->img->top_margin,$btotrequired);
}
}
}
 
// Stroke the graph
// $aStrokeFileName If != "" the image will be written to this file and NOT
// streamed back to the browser
function Stroke($aStrokeFileName="") {
 
// Start by adjusting the margin so that potential titles will fit.
$this->AdjustMarginsForTitles();
 
// If the filename is the predefined value = '_csim_special_'
// we assume that the call to stroke only needs to do enough
// to correctly generate the CSIM maps.
// We use this variable to skip things we don't strictly need
// to do to generate the image map to improve performance
// a best we can. Therefor you will see a lot of tests !$_csim in the
// code below.
$_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
 
// We need to know if we have stroked the plot in the
// GetCSIMareas. Otherwise the CSIM hasn't been generated
// and in the case of GetCSIM called before stroke to generate
// CSIM without storing an image to disk GetCSIM must call Stroke.
$this->iHasStroked = true;
 
// Do any pre-stroke adjustment that is needed by the different plot types
// (i.e bar plots want's to add an offset to the x-labels etc)
for($i=0; $i<count($this->plots) ; ++$i ) {
$this->plots[$i]->PreStrokeAdjust($this);
$this->plots[$i]->DoLegend($this);
}
// Any plots on the second Y scale?
if( $this->y2scale != null ) {
for($i=0; $i<count($this->y2plots) ; ++$i ) {
$this->y2plots[$i]->PreStrokeAdjust($this);
$this->y2plots[$i]->DoLegend($this);
}
}
// Bail out if any of the Y-axis not been specified and
// has no plots. (This means it is impossible to do autoscaling and
// no other scale was given so we can't possible draw anything). If you use manual
// scaling you also have to supply the tick steps as well.
if( (!$this->yscale->IsSpecified() && count($this->plots)==0) ||
($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) {
$e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
$e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
$e .= "2. Specified a scale manually but have forgot to specify the tick steps";
JpGraphError::Raise($e);
}
// Bail out if no plots and no specified X-scale
if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
JpGraphError::Raise("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
 
//Check if we should autoscale y-axis
if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
list($min,$max) = $this->GetPlotsYMinMax($this->plots);
$lres = $this->GetLinesYMinMax($this->lines);
if( $lres ) {
list($linmin,$linmax) = $lres ;
$min = min($min,$linmin);
$max = max($max,$linmax);
}
$tres = $this->GetTextsYMinMax();
if( $tres ) {
list($tmin,$tmax) = $tres ;
$min = min($min,$tmin);
$max = max($max,$tmax);
}
$this->yscale->AutoScale($this->img,$min,$max,
$this->img->plotheight/$this->ytick_factor);
}
elseif( $this->yscale->IsSpecified() &&
( $this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified()) ) {
// The tick calculation will use the user suplied min/max values to determine
// the ticks. If auto_ticks is false the exact user specifed min and max
// values will be used for the scale.
// If auto_ticks is true then the scale might be slightly adjusted
// so that the min and max values falls on an even major step.
$min = $this->yscale->scale[0];
$max = $this->yscale->scale[1];
$this->yscale->AutoScale($this->img,$min,$max,
$this->img->plotheight/$this->ytick_factor,
$this->yscale->auto_ticks);
}
 
if( $this->y2scale != null) {
if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) {
list($min,$max) = $this->GetPlotsYMinMax($this->y2plots);
$this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
}
elseif( $this->y2scale->IsSpecified() &&
( $this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified()) ) {
// The tick calculation will use the user suplied min/max values to determine
// the ticks. If auto_ticks is false the exact user specifed min and max
// values will be used for the scale.
// If auto_ticks is true then the scale might be slightly adjusted
// so that the min and max values falls on an even major step.
$min = $this->y2scale->scale[0];
$max = $this->y2scale->scale[1];
$this->y2scale->AutoScale($this->img,$min,$max,
$this->img->plotheight/$this->ytick_factor,
$this->y2scale->auto_ticks);
}
}
//Check if we should autoscale x-axis
if( !$this->xscale->IsSpecified() ) {
if( substr($this->axtype,0,4) == "text" ) {
$max=0;
foreach( $this->plots as $p ) {
$max=max($max,$p->numpoints-1);
}
$min=0;
if( $this->y2axis != null ) {
foreach( $this->y2plots as $p ) {
$max=max($max,$p->numpoints-1);
}
}
$this->xscale->Update($this->img,$min,$max);
$this->xscale->ticks->Set($this->xaxis->tick_step,1);
$this->xscale->ticks->SupressMinorTickMarks();
}
else {
list($min,$max) = $this->GetXMinMax();
$lres = $this->GetLinesXMinMax($this->lines);
if( $lres ) {
list($linmin,$linmax) = $lres ;
$min = min($min,$linmin);
$max = max($max,$linmax);
}
$tres = $this->GetTextsXMinMax();
if( $tres ) {
list($tmin,$tmax) = $tres ;
$min = min($min,$tmin);
$max = max($max,$tmax);
}
$this->xscale->AutoScale($this->img,$min,$max,$this->img->plotwidth/$this->xtick_factor);
}
//Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
if( !is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos) )
$this->yaxis->SetPos($this->xscale->GetMinVal());
if( $this->y2axis != null ) {
if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
$this->y2axis->SetPos($this->xscale->GetMaxVal());
$this->y2axis->SetTitleSide(SIDE_RIGHT);
}
}
elseif( $this->xscale->IsSpecified() &&
( $this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified()) ) {
// The tick calculation will use the user suplied min/max values to determine
// the ticks. If auto_ticks is false the exact user specifed min and max
// values will be used for the scale.
// If auto_ticks is true then the scale might be slightly adjusted
// so that the min and max values falls on an even major step.
$min = $this->xscale->scale[0];
$max = $this->xscale->scale[1];
$this->xscale->AutoScale($this->img,$min,$max,
$this->img->plotwidth/$this->xtick_factor,
false);
 
if( $this->y2axis != null ) {
if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
$this->y2axis->SetPos($this->xscale->GetMaxVal());
$this->y2axis->SetTitleSide(SIDE_RIGHT);
}
 
}
// If we have a negative values and x-axis position is at 0
// we need to supress the first and possible the last tick since
// they will be drawn on top of the y-axis (and possible y2 axis)
// The test below might seem strange the reasone being that if
// the user hasn't specified a value for position this will not
// be set until we do the stroke for the axis so as of now it
// is undefined.
// For X-text scale we ignore all this since the tick are usually
// much further in and not close to the Y-axis. Hence the test
// for 'text'
 
if( ($this->yaxis->pos==$this->xscale->GetMinVal() ||
(is_string($this->yaxis->pos) && $this->yaxis->pos=='min')) &&
!is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 &&
substr($this->axtype,0,4) != 'text' && $this->xaxis->pos!="min" ) {
 
//$this->yscale->ticks->SupressZeroLabel(false);
$this->xscale->ticks->SupressFirst();
if( $this->y2axis != null ) {
$this->xscale->ticks->SupressLast();
}
}
elseif( !is_numeric($this->yaxis->pos) && $this->yaxis->pos=='max' ) {
$this->xscale->ticks->SupressLast();
}
 
if( !$_csim ) {
$this->StrokePlotArea();
$this->StrokeAxis();
}
 
// Stroke bands
if( $this->bands != null && !$_csim)
for($i=0; $i<count($this->bands); ++$i) {
// Stroke all bands that asks to be in the background
if( $this->bands[$i]->depth == DEPTH_BACK )
$this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
}
 
if( $this->grid_depth == DEPTH_BACK && !$_csim) {
$this->ygrid->Stroke();
$this->xgrid->Stroke();
}
// Stroke Y2-axis
if( $this->y2axis != null && !$_csim) {
$this->y2axis->Stroke($this->xscale);
$this->y2grid->Stroke();
}
$oldoff=$this->xscale->off;
if(substr($this->axtype,0,4)=="text") {
$this->xscale->off +=
ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step);
}
 
if( $this->iDoClipping ) {
$oldimage = $this->img->CloneCanvasH();
}
 
if( ! $this->y2orderback ) {
// Stroke all plots for Y1 axis
for($i=0; $i < count($this->plots); ++$i) {
$this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
$this->plots[$i]->StrokeMargin($this->img);
}
}
 
// Stroke all plots for Y2 axis
if( $this->y2scale != null )
for($i=0; $i< count($this->y2plots); ++$i ) {
$this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
}
 
if( $this->y2orderback ) {
// Stroke all plots for Y1 axis
for($i=0; $i < count($this->plots); ++$i) {
$this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
$this->plots[$i]->StrokeMargin($this->img);
}
}
 
 
if( $this->iDoClipping ) {
// Clipping only supports graphs at 0 and 90 degrees
if( $this->img->a == 0 ) {
$this->img->CopyCanvasH($oldimage,$this->img->img,
$this->img->left_margin,$this->img->top_margin,
$this->img->left_margin,$this->img->top_margin,
$this->img->plotwidth+1,$this->img->plotheight);
}
elseif( $this->img->a == 90 ) {
$adj = ($this->img->height - $this->img->width)/2;
$this->img->CopyCanvasH($oldimage,$this->img->img,
$this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
$this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
$this->img->plotheight+1,$this->img->plotwidth);
}
else {
JpGraphError::Raise('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.');
}
$this->img->Destroy();
$this->img->SetCanvasH($oldimage);
}
 
$this->xscale->off=$oldoff;
if( $this->grid_depth == DEPTH_FRONT && !$_csim ) {
$this->ygrid->Stroke();
$this->xgrid->Stroke();
}
 
// Stroke bands
if( $this->bands!= null )
for($i=0; $i<count($this->bands); ++$i) {
// Stroke all bands that asks to be in the foreground
if( $this->bands[$i]->depth == DEPTH_FRONT )
$this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
}
 
// Stroke any lines added
if( $this->lines != null ) {
for($i=0; $i<count($this->lines); ++$i) {
$this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
}
}
// Finally draw the axis again since some plots may have nagged
// the axis in the edges.
if( !$_csim )
$this->StrokeAxis();
 
if( $this->y2scale != null && !$_csim )
$this->y2axis->Stroke($this->xscale);
if( !$_csim ) {
$this->StrokePlotBox();
}
if( !$_csim ) {
// The titles and legends never gets rotated so make sure
// that the angle is 0 before stroking them
$aa = $this->img->SetAngle(0);
$this->StrokeTitles();
$this->footer->Stroke($this->img);
}
 
$this->legend->Stroke($this->img);
 
if( !$_csim ) {
 
$this->StrokeTexts();
$this->img->SetAngle($aa);
// Draw an outline around the image map
if(JPG_DEBUG)
$this->DisplayClientSideaImageMapAreas();
// Adjust the appearance of the image
$this->AdjustSaturationBrightnessContrast();
 
// If the filename is given as the special "__handle"
// then the image handler is returned and the image is NOT
// streamed back
if( $aStrokeFileName == _IMG_HANDLER ) {
return $this->img->img;
}
else {
// Finally stream the generated picture
$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
$aStrokeFileName);
}
}
}
 
//---------------
// PRIVATE METHODS
function StrokeAxis() {
// Stroke axis
if( $this->iAxisStyle != AXSTYLE_SIMPLE ) {
switch( $this->iAxisStyle ) {
case AXSTYLE_BOXIN :
$toppos = SIDE_DOWN;
$bottompos = SIDE_UP;
$leftpos = SIDE_RIGHT;
$rightpos = SIDE_LEFT;
break;
case AXSTYLE_BOXOUT :
$toppos = SIDE_UP;
$bottompos = SIDE_DOWN;
$leftpos = SIDE_LEFT;
$rightpos = SIDE_RIGHT;
break;
case AXSTYLE_YBOXIN:
$toppos = -100;
$bottompos = SIDE_UP;
$leftpos = SIDE_RIGHT;
$rightpos = SIDE_LEFT;
break;
case AXSTYLE_YBOXOUT:
$toppos = -100;
$bottompos = SIDE_DOWN;
$leftpos = SIDE_LEFT;
$rightpos = SIDE_RIGHT;
break;
default:
JpGRaphError::Raise('Unknown AxisStyle() : '.$this->iAxisStyle);
break;
}
$this->xaxis->SetPos('min');
// By default we hide the first label so it doesn't cross the
// Y-axis in case the positon hasn't been set by the user.
// However, if we use a box we always want the first value
// displayed so we make sure it will be displayed.
$this->xscale->ticks->SupressFirst(false);
$this->xaxis->SetLabelSide(SIDE_DOWN);
$this->xaxis->scale->ticks->SetSide($bottompos);
$this->xaxis->Stroke($this->yscale);
 
if( $toppos != -100 ) {
// To avoid side effects we work on a new copy
$maxis = $this->xaxis;
$maxis->SetPos('max');
$maxis->SetLabelSide(SIDE_UP);
$maxis->SetLabelMargin(7);
$this->xaxis->scale->ticks->SetSide($toppos);
$maxis->Stroke($this->yscale);
}
 
$this->yaxis->SetPos('min');
$this->yaxis->SetLabelMargin(10);
$this->yaxis->SetLabelSide(SIDE_LEFT);
$this->yaxis->scale->ticks->SetSide($leftpos);
$this->yaxis->Stroke($this->xscale);
 
$myaxis = $this->yaxis;
$myaxis->SetPos('max');
$myaxis->SetLabelMargin(10);
$myaxis->SetLabelSide(SIDE_RIGHT);
$myaxis->title->Set('');
$myaxis->scale->ticks->SetSide($rightpos);
$myaxis->Stroke($this->xscale);
}
else {
$this->xaxis->Stroke($this->yscale);
$this->yaxis->Stroke($this->xscale);
}
}
 
 
// Private helper function for backgound image
function LoadBkgImage($aImgFormat='',$aFile='') {
if( $aFile == '' )
$aFile = $this->background_image;
// Remove case sensitivity and setup appropriate function to create image
// Get file extension. This should be the LAST '.' separated part of the filename
$e = explode('.',$aFile);
$ext = strtolower($e[count($e)-1]);
if ($ext == "jpeg") {
$ext = "jpg";
}
if( trim($ext) == '' )
$ext = 'png'; // Assume PNG if no extension specified
 
if( $aImgFormat == '' )
$imgtag = $ext;
else
$imgtag = $aImgFormat;
 
if( $imgtag == "jpg" || $imgtag == "jpeg")
{
$f = "imagecreatefromjpeg";
$imgtag = "jpg";
}
else
{
$f = "imagecreatefrom".$imgtag;
}
 
// Compare specified image type and file extension
if( $imgtag != $ext ) {
$t = " Background image seems to be of different type (has different file extension)".
" than specified imagetype. <br>Specified: '".
$aImgFormat."'<br>File: '".$aFile."'";
JpGraphError::Raise($t);
}
 
$img = @$f($aFile);
if( !$img ) {
JpGraphError::Raise(" Can't read background image: '".$aFile."'");
}
return $img;
}
 
function StrokeBackgroundGrad() {
if( $this->bkg_gradtype < 0 )
return;
$grad = new Gradient($this->img);
if( $this->bkg_gradstyle == BGRAD_PLOT ) {
$xl = $this->img->left_margin;
$yt = $this->img->top_margin;
$xr = $xl + $this->img->plotwidth ;
$yb = $yt + $this->img->plotheight ;
}
else {
$xl = 0;
$yt = 0;
$xr = $xl + $this->img->width - 1;
$yb = $yt + $this->img->height - 1;
}
if( $this->doshadow ) {
$xr -= $this->shadow_width;
$yb -= $this->shadow_width;
}
$grad->FilledRectangle($xl,$yt,$xr,$yb,
$this->bkg_gradfrom,$this->bkg_gradto,
$this->bkg_gradtype);
}
 
function StrokeFrameBackground() {
if( $this->background_image == "" )
return;
 
$bkgimg = $this->LoadBkgImage($this->background_image_format);
$this->img->_AdjBrightContrast($bkgimg,$this->background_image_bright,
$this->background_image_contr);
$this->img->_AdjSat($bkgimg,$this->background_image_sat);
$bw = ImageSX($bkgimg);
$bh = ImageSY($bkgimg);
 
// No matter what the angle is we always stroke the image and frame
// assuming it is 0 degree
$aa = $this->img->SetAngle(0);
switch( $this->background_image_type ) {
case BGIMG_FILLPLOT: // Resize to just fill the plotarea
$this->StrokeFrame();
$GLOBALS['copyfunc']($this->img->img,$bkgimg,
$this->img->left_margin,$this->img->top_margin,
0,0,$this->img->plotwidth,$this->img->plotheight,
$bw,$bh);
break;
case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit
$GLOBALS['copyfunc']($this->img->img,$bkgimg,
0,0,0,0,
$this->img->width,$this->img->height,
$bw,$bh);
$this->StrokeFrame();
break;
case BGIMG_COPY: // Just copy the image from left corner, no resizing
$GLOBALS['copyfunc']($this->img->img,$bkgimg,
0,0,0,0,
$bw,$bh,
$bw,$bh);
$this->StrokeFrame();
break;
case BGIMG_CENTER: // Center original image in the plot area
$centerx = round($this->img->plotwidth/2+$this->img->left_margin-$bw/2);
$centery = round($this->img->plotheight/2+$this->img->top_margin-$bh/2);
$GLOBALS['copyfunc']($this->img->img,$bkgimg,
$centerx,$centery,
0,0,
$bw,$bh,
$bw,$bh);
$this->StrokeFrame();
break;
default:
JpGraphError::Raise(" Unknown background image layout");
}
$this->img->SetAngle($aa);
}
 
// Private
// Draw a frame around the image
function StrokeFrame() {
if( !$this->doframe ) return;
if( $this->background_image_type <= 1 &&
($this->bkg_gradtype < 0 ||
($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_PLOT) ) )
$c = $this->margin_color;
else
$c = false;
if( $this->doshadow ) {
$this->img->SetColor($this->frame_color);
$this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height,
$c,$this->shadow_width,$this->shadow_color);
}
elseif( $this->framebevel ) {
if( $c ) {
$this->img->SetColor($this->margin_color);
$this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
}
$this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
$this->framebeveldepth,
$this->framebevelcolor1,$this->framebevelcolor2);
if( $this->framebevelborder ) {
$this->img->SetColor($this->framebevelbordercolor);
$this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
}
}
else {
$this->img->SetLineWeight($this->frame_weight);
if( $c ) {
$this->img->SetColor($this->margin_color);
$this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
}
$this->img->SetColor($this->frame_color);
$this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
}
$this->StrokeBackgroundGrad();
}
 
// Stroke the plot area with either a solid color or a background image
function StrokePlotArea() {
// Note: To be consistent we really should take a possible shadow
// into account. However, that causes some problem for the LinearScale class
// since in the current design it does not have any links to class Graph which
// means it has no way of compensating for the adjusted plotarea in case of a
// shadow. So, until I redesign LinearScale we can't compensate for this.
// So just set the two adjustment parameters to zero for now.
$boxadj = 0; //$this->doframe ? $this->frame_weight : 0 ;
$adj = 0; //$this->doshadow ? $this->shadow_width : 0 ;
 
if( $this->background_image != "" ) {
$this->StrokeFrameBackground();
}
else {
$aa = $this->img->SetAngle(0);
$this->StrokeFrame();
if( $this->bkg_gradtype < 0 ||
($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_MARGIN ) ) {
$this->img->SetAngle($aa);
$this->img->PushColor($this->plotarea_color);
$this->img->FilledRectangle($this->img->left_margin+$boxadj,
$this->img->top_margin+$boxadj,
$this->img->width-$this->img->right_margin-$adj-2*$boxadj,
$this->img->height-$this->img->bottom_margin-$adj-2*$boxadj);
$this->img->PopColor();
}
}
}
function StrokePlotBox() {
// Should we draw a box around the plot area?
if( $this->boxed ) {
$this->img->SetLineWeight($this->box_weight);
$this->img->SetColor($this->box_color);
$this->img->Rectangle(
$this->img->left_margin,$this->img->top_margin,
$this->img->width-$this->img->right_margin,
$this->img->height-$this->img->bottom_margin);
}
}
 
function SetTitleBackgroundFillStyle($aStyle,$aColor1='black',$aColor2='white') {
$this->titlebkg_fillstyle = $aStyle;
$this->titlebkg_scolor1 = $aColor1;
$this->titlebkg_scolor2 = $aColor2;
}
 
function SetTitleBackground($aBackColor='gray', $aStyle=TITLEBKG_STYLE1, $aFrameStyle=TITLEBKG_FRAME_NONE, $aFrameColor='black', $aFrameWeight=1, $aBevelHeight=3, $aEnable=true) {
$this->titlebackground = $aEnable;
$this->titlebackground_color = $aBackColor;
$this->titlebackground_style = $aStyle;
$this->titlebackground_framecolor = $aFrameColor;
$this->titlebackground_framestyle = $aFrameStyle;
$this->titlebackground_frameweight = $aFrameWeight;
$this->titlebackground_bevelheight = $aBevelHeight ;
}
 
 
function StrokeTitles() {
 
$margin=3;
 
if( $this->titlebackground ) {
 
// Find out height
$this->title->margin += 2 ;
$h = $this->title->GetTextHeight($this->img)+$this->title->margin+$margin;
if( $this->subtitle->t != "" && !$this->subtitle->hide ) {
$h += $this->subtitle->GetTextHeight($this->img)+$margin+
$this->subtitle->margin;
}
if( $this->subsubtitle->t != "" && !$this->subsubtitle->hide ) {
$h += $this->subsubtitle->GetTextHeight($this->img)+$margin+
$this->subsubtitle->margin;
}
$this->img->PushColor($this->titlebackground_color);
if( $this->titlebackground_style === 1 ) {
// Inside the frame
if( $this->framebevel ) {
$x1 = $y1 = $this->framebeveldepth + 1 ;
$x2 = $this->img->width - $this->framebeveldepth - 2 ;
$this->title->margin += $this->framebeveldepth + 1 ;
$h += $y1 ;
}
else {
$x1 = $y1 = $this->frame_weight;
$x2 = $this->img->width - 2*$x1;
}
}
elseif( $this->titlebackground_style === 2 ) {
// Cover the frame as well
$x1 = $y1 = 0;
$x2 = $this->img->width - 1 ;
}
elseif( $this->titlebackground_style === 3) {
// Cover the frame as well (the difference is that
// for style==3 a bevel frame border is on top
// of the title background)
$x1 = $y1 = 0;
$x2 = $this->img->width - 1 ;
$h += $this->framebeveldepth ;
$this->title->margin += $this->framebeveldepth ;
}
else {
JpGraphError::Raise('Unknown title background style.');
}
 
if( $this->titlebackground_framestyle === 3 ) {
$h += $this->titlebackground_bevelheight*2 + 1 ;
$this->title->margin += $this->titlebackground_bevelheight ;
}
 
if( $this->doshadow ) {
$x2 -= $this->shadow_width ;
}
if( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_HSTRIPED ) {
$this->img->FilledRectangle2($x1,$y1,$x2,$h,
$this->titlebkg_scolor1,
$this->titlebkg_scolor2);
}
elseif( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_VSTRIPED ) {
$this->img->FilledRectangle2($x1,$y1,$x2,$h,
$this->titlebkg_scolor1,
$this->titlebkg_scolor2,2);
}
else {
// Solid fill
$this->img->FilledRectangle($x1,$y1,$x2,$h);
}
$this->img->PopColor();
 
$this->img->PushColor($this->titlebackground_framecolor);
$this->img->SetLineWeight($this->titlebackground_frameweight);
if( $this->titlebackground_framestyle == 1 ) {
// Frame background
$this->img->Rectangle($x1,$y1,$x2,$h);
}
elseif( $this->titlebackground_framestyle == 2 ) {
// Bottom line only
$this->img->Line($x1,$h,$x2,$h);
}
elseif( $this->titlebackground_framestyle == 3 ) {
$this->img->Bevel($x1,$y1,$x2,$h,$this->titlebackground_bevelheight);
}
$this->img->PopColor();
 
// This is clumsy. But we neeed to stroke the whole graph frame if it is
// set to bevel to get the bevel shading on top of the text background
if( $this->framebevel && $this->doframe &&
$this->titlebackground_style === 3 ) {
$this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
$this->framebeveldepth,
$this->framebevelcolor1,$this->framebevelcolor2);
if( $this->framebevelborder ) {
$this->img->SetColor($this->framebevelbordercolor);
$this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
}
}
}
 
// Stroke title
$y = $this->title->margin;
$this->title->Center(0,$this->img->width,$y);
$this->title->Stroke($this->img);
// ... and subtitle
$y += $this->title->GetTextHeight($this->img) + $margin + $this->subtitle->margin;
$this->subtitle->Center(0,$this->img->width,$y);
$this->subtitle->Stroke($this->img);
 
// ... and subsubtitle
$y += $this->subtitle->GetTextHeight($this->img) + $margin + $this->subsubtitle->margin;
$this->subsubtitle->Center(0,$this->img->width,$y);
$this->subsubtitle->Stroke($this->img);
 
// ... and fancy title
$this->tabtitle->Stroke($this->img);
 
}
 
function StrokeTexts() {
// Stroke any user added text objects
if( $this->texts != null ) {
for($i=0; $i<count($this->texts); ++$i) {
$this->texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->yscale);
}
}
}
 
function DisplayClientSideaImageMapAreas() {
// Debug stuff - display the outline of the image map areas
foreach ($this->plots as $p) {
$csim.= $p->GetCSIMareas();
}
$csim .= $this->legend->GetCSIMareas();
if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
$this->img->SetColor($this->csimcolor);
for ($i=0; $i<count($coords[0]); $i++) {
if ($coords[1][$i]=="poly") {
preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
$this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
for ($j=0; $j<count($pts[0]); $j++) {
$this->img->LineTo($pts[1][$j],$pts[2][$j]);
}
} else if ($coords[1][$i]=="rect") {
$pts = preg_split('/,/', $coords[2][$i]);
$this->img->SetStartPoint($pts[0],$pts[1]);
$this->img->LineTo($pts[2],$pts[1]);
$this->img->LineTo($pts[2],$pts[3]);
$this->img->LineTo($pts[0],$pts[3]);
$this->img->LineTo($pts[0],$pts[1]);
}
}
}
}
 
function AdjustSaturationBrightnessContrast() {
// Adjust the brightness and contrast of the image
if( $this->image_contr || $this->image_bright )
$this->img->AdjBrightContrast($this->image_bright,$this->image_contr);
if( $this->image_sat )
$this->img->AdjSat($this->image_sat);
}
 
// Text scale offset in world coordinates
function SetTextScaleOff($aOff) {
$this->text_scale_off = $aOff;
$this->xscale->text_scale_off = $aOff;
}
 
// Get Y min and max values for added lines
function GetLinesYMinMax( $aLines ) {
$n = count($aLines);
if( $n == 0 ) return false;
$min = $aLines[0]->scaleposition ;
$max = $min ;
$flg = false;
for( $i=0; $i < $n; ++$i ) {
if( $aLines[$i]->direction == HORIZONTAL ) {
$flg = true ;
$v = $aLines[$i]->scaleposition ;
if( $min > $v ) $min = $v ;
if( $max < $v ) $max = $v ;
}
}
return $flg ? array($min,$max) : false ;
}
 
// Get X min and max values for added lines
function GetLinesXMinMax( $aLines ) {
$n = count($aLines);
if( $n == 0 ) return false ;
$min = $aLines[0]->scaleposition ;
$max = $min ;
$flg = false;
for( $i=0; $i < $n; ++$i ) {
if( $aLines[$i]->direction == VERTICAL ) {
$flg = true ;
$v = $aLines[$i]->scaleposition ;
if( $min > $v ) $min = $v ;
if( $max < $v ) $max = $v ;
}
}
return $flg ? array($min,$max) : false ;
}
 
// Get min and max values for all included plots
function GetPlotsYMinMax(&$aPlots) {
list($xmax,$max) = $aPlots[0]->Max();
list($xmin,$min) = $aPlots[0]->Min();
for($i=0; $i<count($aPlots); ++$i ) {
list($xmax,$ymax)=$aPlots[$i]->Max();
list($xmin,$ymin)=$aPlots[$i]->Min();
if (!is_string($ymax) || $ymax != "") $max=max($max,$ymax);
if (!is_string($ymin) || $ymin != "") $min=min($min,$ymin);
}
if( $min == "" ) $min = 0;
if( $max == "" ) $max = 0;
if( $min == 0 && $max == 0 ) {
// Special case if all values are 0
$min=0;$max=1;
}
return array($min,$max);
}
 
} // Class
 
 
//===================================================
// CLASS TTF
// Description: Handle TTF font names
//===================================================
class TTF {
var $font_files,$style_names;
//---------------
// CONSTRUCTOR
function TTF() {
$this->style_names=array(FS_NORMAL=>'normal',FS_BOLD=>'bold',FS_ITALIC=>'italic',FS_BOLDITALIC=>'bolditalic');
// File names for available fonts
$this->font_files=array(
FF_COURIER => array(FS_NORMAL=>'cour', FS_BOLD=>'courbd', FS_ITALIC=>'couri', FS_BOLDITALIC=>'courbi' ),
FF_GEORGIA => array(FS_NORMAL=>'georgia', FS_BOLD=>'georgiab', FS_ITALIC=>'georgiai', FS_BOLDITALIC=>'' ),
FF_TREBUCHE =>array(FS_NORMAL=>'trebuc', FS_BOLD=>'trebucbd', FS_ITALIC=>'trebucit', FS_BOLDITALIC=>'trebucbi' ),
FF_VERDANA => array(FS_NORMAL=>'verdana', FS_BOLD=>'verdanab', FS_ITALIC=>'verdanai', FS_BOLDITALIC=>'' ),
FF_TIMES => array(FS_NORMAL=>'times', FS_BOLD=>'timesbd', FS_ITALIC=>'timesi', FS_BOLDITALIC=>'timesbi' ),
FF_COMIC => array(FS_NORMAL=>'comic', FS_BOLD=>'comicbd', FS_ITALIC=>'', FS_BOLDITALIC=>'' ),
FF_ARIAL => array(FS_NORMAL=>'arial', FS_BOLD=>'arialbd', FS_ITALIC=>'ariali', FS_BOLDITALIC=>'arialbi' ) ,
FF_SIMSUN => array(FS_NORMAL=>'simsun', FS_BOLD=>'simhei', FS_ITALIC=>'simsun', FS_BOLDITALIC=>'simhei' )
);
}
 
//---------------
// PUBLIC METHODS
// Create the TTF file from the font specification
function File($family,$style=FS_NORMAL) {
if( $family == FF_HANDWRT || $family==FF_BOOK )
JpGraphError::Raise('Font families FF_HANDWRT and FF_BOOK are no longer available due to copyright problem with these fonts. Fonts can no longer be distributed with JpGraph. Please download fonts from http://corefonts.sourceforge.net/');
 
$fam = @$this->font_files[$family];
if( !$fam ) JpGraphError::Raise("Specified TTF font family (id=$family) is unknown or does not exist. ".
"Please note that TTF fonts are not distributed with JpGraph for copyright reasons.".
" You can find the MS TTF WEB-fonts (arial, courier etc) for download at ".
" http://corefonts.sourceforge.net/");
$f = @$fam[$style];
 
if( $f==='' )
JpGraphError::Raise('Style "'.$this->style_names[$style].'" is not available for font family '.$this->font_files[$family][FS_NORMAL].'.');
if( !$f )
JpGraphError::Raise("Unknown font style specification [$fam].");
$f = TTF_DIR.$f.'.ttf';
if( file_exists($f) === false || is_readable($f) === false ) {
JpGraphError::Raise("Font file \"$f\" is not readable or does not exist.");
}
return $f;
}
} // Class
 
//===================================================
// CLASS LineProperty
// Description: Holds properties for a line
//===================================================
class LineProperty {
var $iWeight=1, $iColor="black",$iStyle="solid";
var $iShow=true;
//---------------
// PUBLIC METHODS
function SetColor($aColor) {
$this->iColor = $aColor;
}
function SetWeight($aWeight) {
$this->iWeight = $aWeight;
}
function SetStyle($aStyle) {
$this->iStyle = $aStyle;
}
function Show($aShow=true) {
$this->iShow=$aShow;
}
function Stroke($aImg,$aX1,$aY1,$aX2,$aY2) {
if( $this->iShow ) {
$aImg->SetColor($this->iColor);
$aImg->SetLineWeight($this->iWeight);
$aImg->SetLineStyle($this->iStyle);
$aImg->StyleLine($aX1,$aY1,$aX2,$aY2);
}
}
}
 
 
//===================================================
// CLASS Text
// Description: Arbitrary text object that can be added to the graph
//===================================================
class Text {
var $t,$x=0,$y=0,$halign="left",$valign="top",$color=array(0,0,0);
var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
var $hide=false, $dir=0;
var $boxed=false; // Should the text be boxed
var $paragraph_align="left";
var $margin;
var $icornerradius=0,$ishadowwidth=3;
var $iScalePosY=null,$iScalePosX=null;
 
//---------------
// CONSTRUCTOR
 
// Create new text at absolute pixel coordinates
function Text($aTxt="",$aXAbsPos=0,$aYAbsPos=0) {
$this->t = $aTxt;
$this->x = round($aXAbsPos);
$this->y = round($aYAbsPos);
$this->margin = 0;
}
//---------------
// PUBLIC METHODS
// Set the string in the text object
function Set($aTxt) {
$this->t = $aTxt;
}
// Alias for Pos()
function SetPos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
$this->Pos($aXAbsPos,$aYAbsPos,$aHAlign,$aVAlign);
}
// Specify the position and alignment for the text object
function Pos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
$this->x = $aXAbsPos;
$this->y = $aYAbsPos;
$this->halign = $aHAlign;
$this->valign = $aVAlign;
}
 
function SetScalePos($aX,$aY) {
$this->iScalePosX = $aX;
$this->iScalePosY = $aY;
}
// Specify alignment for the text
function Align($aHAlign,$aVAlign="top",$aParagraphAlign="") {
$this->halign = $aHAlign;
$this->valign = $aVAlign;
if( $aParagraphAlign != "" )
$this->paragraph_align = $aParagraphAlign;
}
// Alias
function SetAlign($aHAlign,$aVAlign="top",$aParagraphAlign="") {
$this->Align($aHAlign,$aVAlign,$aParagraphAlign);
}
 
// Specifies the alignment for a multi line text
function ParagraphAlign($aAlign) {
$this->paragraph_align = $aAlign;
}
 
function SetShadow($aShadowColor='gray',$aShadowWidth=3) {
$this->ishadowwidth=$aShadowWidth;
$this->shadow=$aShadowColor;
$this->boxed=true;
}
// Specify that the text should be boxed. fcolor=frame color, bcolor=border color,
// $shadow=drop shadow should be added around the text.
function SetBox($aFrameColor=array(255,255,255),$aBorderColor=array(0,0,0),$aShadowColor=false,$aCornerRadius=4,$aShadowWidth=3) {
if( $aFrameColor==false )
$this->boxed=false;
else
$this->boxed=true;
$this->fcolor=$aFrameColor;
$this->bcolor=$aBorderColor;
// For backwards compatibility when shadow was just true or false
if( $aShadowColor === true )
$aShadowColor = 'gray';
$this->shadow=$aShadowColor;
$this->icornerradius=$aCornerRadius;
$this->ishadowwidth=$aShadowWidth;
}
// Hide the text
function Hide($aHide=true) {
$this->hide=$aHide;
}
// This looks ugly since it's not a very orthogonal design
// but I added this "inverse" of Hide() to harmonize
// with some classes which I designed more recently (especially)
// jpgraph_gantt
function Show($aShow=true) {
$this->hide=!$aShow;
}
// Specify font
function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
$this->font_family=$aFamily;
$this->font_style=$aStyle;
$this->font_size=$aSize;
}
// Center the text between $left and $right coordinates
function Center($aLeft,$aRight,$aYAbsPos=false) {
$this->x = $aLeft + ($aRight-$aLeft )/2;
$this->halign = "center";
if( is_numeric($aYAbsPos) )
$this->y = $aYAbsPos;
}
// Set text color
function SetColor($aColor) {
$this->color = $aColor;
}
function SetAngle($aAngle) {
$this->SetOrientation($aAngle);
}
// Orientation of text. Note only TTF fonts can have an arbitrary angle
function SetOrientation($aDirection=0) {
if( is_numeric($aDirection) )
$this->dir=$aDirection;
elseif( $aDirection=="h" )
$this->dir = 0;
elseif( $aDirection=="v" )
$this->dir = 90;
else JpGraphError::Raise(" Invalid direction specified for text.");
}
// Total width of text
function GetWidth($aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$w = $aImg->GetTextWidth($this->t,$this->dir);
return $w;
}
// Hight of font
function GetFontHeight($aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$h = $aImg->GetFontHeight();
return $h;
 
}
 
function GetTextHeight($aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$h = $aImg->GetTextHeight($this->t,$this->dir);
return $h;
}
 
// Set the margin which will be interpretated differently depending
// on the context.
function SetMargin($aMarg) {
$this->margin = $aMarg;
}
 
function StrokeWithScale($aImg,$axscale,$ayscale) {
if( $this->iScalePosX === null ||
$this->iScalePosY === null ) {
$this->Stroke($aImg);
}
else {
$this->Stroke($aImg,
round($axscale->Translate($this->iScalePosX)),
round($ayscale->Translate($this->iScalePosY)));
}
}
// Display text in image
function Stroke($aImg,$x=null,$y=null) {
 
if( !empty($x) ) $this->x = round($x);
if( !empty($y) ) $this->y = round($y);
 
// If position been given as a fraction of the image size
// calculate the absolute position
if( $this->x < 1 && $this->x > 0 ) $this->x *= $aImg->width;
if( $this->y < 1 && $this->y > 0 ) $this->y *= $aImg->height;
 
$aImg->PushColor($this->color);
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$aImg->SetTextAlign($this->halign,$this->valign);
if( $this->boxed ) {
if( $this->fcolor=="nofill" )
$this->fcolor=false;
$aImg->SetLineWeight(1);
$aImg->StrokeBoxedText($this->x,$this->y,$this->t,
$this->dir,$this->fcolor,$this->bcolor,$this->shadow,
$this->paragraph_align,6,2,$this->icornerradius,
$this->ishadowwidth);
}
else {
$aImg->StrokeText($this->x,$this->y,$this->t,$this->dir,
$this->paragraph_align);
}
$aImg->PopColor($this->color);
}
} // Class
 
class GraphTabTitle extends Text{
var $corner = 6 , $posx = 7, $posy = 4;
var $color='darkred',$fillcolor='lightyellow',$bordercolor='black';
var $align = 'left', $width=TABTITLE_WIDTHFIT;
function GraphTabTitle() {
$this->t = '';
$this->font_style = FS_BOLD;
$this->hide = true;
}
 
function SetColor($aTxtColor,$aFillColor='lightyellow',$aBorderColor='black') {
$this->color = $aTxtColor;
$this->fillcolor = $aFillColor;
$this->bordercolor = $aBorderColor;
}
 
function SetFillColor($aFillColor) {
$this->fillcolor = $aFillColor;
}
 
function SetTabAlign($aAlign) {
// Synonym for SetPos
$this->align = $aAlign;
}
 
function SetPos($aAlign) {
$this->align = $aAlign;
}
function SetWidth($aWidth) {
$this->width = $aWidth ;
}
 
function Set($t) {
$this->t = $t;
$this->hide = false;
}
 
function SetCorner($aD) {
$this->corner = $aD ;
}
 
function Stroke($aImg) {
if( $this->hide )
return;
$this->boxed = false;
$w = $this->GetWidth($aImg) + 2*$this->posx;
$h = $this->GetTextHeight($aImg) + 2*$this->posy;
 
$x = $aImg->left_margin;
$y = $aImg->top_margin;
 
if( $this->width === TABTITLE_WIDTHFIT ) {
if( $this->align == 'left' ) {
$p = array($x, $y,
$x, $y-$h+$this->corner,
$x + $this->corner,$y-$h,
$x + $w - $this->corner, $y-$h,
$x + $w, $y-$h+$this->corner,
$x + $w, $y);
}
elseif( $this->align == 'center' ) {
$x += round($aImg->plotwidth/2) - round($w/2);
$p = array($x, $y,
$x, $y-$h+$this->corner,
$x + $this->corner, $y-$h,
$x + $w - $this->corner, $y-$h,
$x + $w, $y-$h+$this->corner,
$x + $w, $y);
}
else {
$x += $aImg->plotwidth -$w;
$p = array($x, $y,
$x, $y-$h+$this->corner,
$x + $this->corner,$y-$h,
$x + $w - $this->corner, $y-$h,
$x + $w, $y-$h+$this->corner,
$x + $w, $y);
}
}
else {
if( $this->width === TABTITLE_WIDTHFULL )
$w = $aImg->plotwidth ;
else
$w = $this->width ;
 
// Make the tab fit the width of the plot area
$p = array($x, $y,
$x, $y-$h+$this->corner,
$x + $this->corner,$y-$h,
$x + $w - $this->corner, $y-$h,
$x + $w, $y-$h+$this->corner,
$x + $w, $y);
}
$aImg->SetTextAlign('left','bottom');
$x += $this->posx;
$y -= $this->posy;
 
$aImg->SetColor($this->fillcolor);
$aImg->FilledPolygon($p);
 
$aImg->SetColor($this->bordercolor);
$aImg->Polygon($p,true);
$aImg->SetColor($this->color);
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$aImg->StrokeText($x,$y,$this->t,0,'center');
}
 
}
 
//===================================================
// CLASS SuperScriptText
// Description: Format a superscript text
//===================================================
class SuperScriptText extends Text {
var $iSuper="";
var $sfont_family="",$sfont_style="",$sfont_size=8;
var $iSuperMargin=2,$iVertOverlap=4,$iSuperScale=0.65;
var $iSDir=0;
var $iSimple=false;
 
function SuperScriptText($aTxt="",$aSuper="",$aXAbsPos=0,$aYAbsPos=0) {
parent::Text($aTxt,$aXAbsPos,$aYAbsPos);
$this->iSuper = $aSuper;
}
 
function FromReal($aVal,$aPrecision=2) {
// Convert a floating point number to scientific notation
$neg=1.0;
if( $aVal < 0 ) {
$neg = -1.0;
$aVal = -$aVal;
}
$l = floor(log10($aVal));
$a = sprintf("%0.".$aPrecision."f",round($aVal / pow(10,$l),$aPrecision));
$a *= $neg;
if( $this->iSimple && ($a == 1 || $a==-1) ) $a = '';
if( $a != '' )
$this->t = $a.' * 10';
else {
if( $neg == 1 )
$this->t = '10';
else
$this->t = '-10';
}
$this->iSuper = $l;
}
 
function Set($aTxt,$aSuper="") {
$this->t = $aTxt;
$this->iSuper = $aSuper;
}
 
function SetSuperFont($aFontFam,$aFontStyle=FS_NORMAL,$aFontSize=8) {
$this->sfont_family = $aFontFam;
$this->sfont_style = $aFontStyle;
$this->sfont_size = $aFontSize;
}
 
// Total width of text
function GetWidth(&$aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$w = $aImg->GetTextWidth($this->t);
$aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
$w += $aImg->GetTextWidth($this->iSuper);
$w += $this->iSuperMargin;
return $w;
}
// Hight of font (approximate the height of the text)
function GetFontHeight(&$aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$h = $aImg->GetFontHeight();
$aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
$h += $aImg->GetFontHeight();
return $h;
}
 
// Hight of text
function GetTextHeight(&$aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$h = $aImg->GetTextHeight($this->t);
$aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
$h += $aImg->GetTextHeight($this->iSuper);
return $h;
}
 
function Stroke($aImg,$ax=-1,$ay=-1) {
// To position the super script correctly we need different
// cases to handle the alignmewnt specified since that will
// determine how we can interpret the x,y coordinates
$w = parent::GetWidth($aImg);
$h = parent::GetTextHeight($aImg);
switch( $this->valign ) {
case 'top':
$sy = $this->y;
break;
case 'center':
$sy = $this->y - $h/2;
break;
case 'bottom':
$sy = $this->y - $h;
break;
default:
JpGraphError::Raise('PANIC: Internal error in SuperScript::Stroke(). Unknown vertical alignment for text');
exit();
}
 
switch( $this->halign ) {
case 'left':
$sx = $this->x + $w;
break;
case 'center':
$sx = $this->x + $w/2;
break;
case 'right':
$sx = $this->x;
break;
default:
JpGraphError::Raise('PANIC: Internal error in SuperScript::Stroke(). Unknown horizontal alignment for text');
exit();
}
 
$sx += $this->iSuperMargin;
$sy += $this->iVertOverlap;
 
// Should we automatically determine the font or
// has the user specified it explicetly?
if( $this->sfont_family == "" ) {
if( $this->font_family <= FF_FONT2 ) {
if( $this->font_family == FF_FONT0 ) {
$sff = FF_FONT0;
}
elseif( $this->font_family == FF_FONT1 ) {
if( $this->font_style == FS_NORMAL )
$sff = FF_FONT0;
else
$sff = FF_FONT1;
}
else {
$sff = FF_FONT1;
}
$sfs = $this->font_style;
$sfz = $this->font_size;
}
else {
// TTF fonts
$sff = $this->font_family;
$sfs = $this->font_style;
$sfz = floor($this->font_size*$this->iSuperScale);
if( $sfz < 8 ) $sfz = 8;
}
$this->sfont_family = $sff;
$this->sfont_style = $sfs;
$this->sfont_size = $sfz;
}
else {
$sff = $this->sfont_family;
$sfs = $this->sfont_style;
$sfz = $this->sfont_size;
}
 
parent::Stroke($aImg,$ax,$ay);
 
 
// For the builtin fonts we need to reduce the margins
// since the bounding bx reported for the builtin fonts
// are much larger than for the TTF fonts.
if( $sff <= FF_FONT2 ) {
$sx -= 2;
$sy += 3;
}
 
$aImg->SetTextAlign('left','bottom');
$aImg->SetFont($sff,$sfs,$sfz);
$aImg->PushColor($this->color);
$aImg->StrokeText($sx,$sy,$this->iSuper,$this->iSDir,'left');
$aImg->PopColor();
}
}
 
 
//===================================================
// CLASS Grid
// Description: responsible for drawing grid lines in graph
//===================================================
class Grid {
var $img;
var $scale;
var $grid_color='#DDDDDD',$grid_mincolor='#DDDDDD';
var $type="solid";
var $show=false, $showMinor=false,$weight=1;
var $fill=false,$fillcolor=array('#EFEFEF','#BBCCFF');
//---------------
// CONSTRUCTOR
function Grid(&$aAxis) {
$this->scale = &$aAxis->scale;
$this->img = &$aAxis->img;
}
//---------------
// PUBLIC METHODS
function SetColor($aMajColor,$aMinColor=false) {
$this->grid_color=$aMajColor;
if( $aMinColor === false )
$aMinColor = $aMajColor ;
$this->grid_mincolor = $aMinColor;
}
function SetWeight($aWeight) {
$this->weight=$aWeight;
}
// Specify if grid should be dashed, dotted or solid
function SetLineStyle($aType) {
$this->type = $aType;
}
// Decide if both major and minor grid should be displayed
function Show($aShowMajor=true,$aShowMinor=false) {
$this->show=$aShowMajor;
$this->showMinor=$aShowMinor;
}
function SetFill($aFlg=true,$aColor1='lightgray',$aColor2='lightblue') {
$this->fill = $aFlg;
$this->fillcolor = array( $aColor1, $aColor2 );
}
// Display the grid
function Stroke() {
if( $this->showMinor ) {
$tmp = $this->grid_color;
$this->grid_color = $this->grid_mincolor;
$this->DoStroke($this->scale->ticks->ticks_pos);
 
$this->grid_color = $tmp;
$this->DoStroke($this->scale->ticks->maj_ticks_pos);
}
else {
$this->DoStroke($this->scale->ticks->maj_ticks_pos);
}
}
//--------------
// Private methods
// Draw the grid
function DoStroke(&$aTicksPos) {
if( !$this->show )
return;
$nbrgrids = count($aTicksPos);
 
if( $this->scale->type=="y" ) {
$xl=$this->img->left_margin;
$xr=$this->img->width-$this->img->right_margin;
if( $this->fill ) {
// Draw filled areas
$y2 = $aTicksPos[0];
$i=1;
while( $i < $nbrgrids ) {
$y1 = $y2;
$y2 = $aTicksPos[$i++];
$this->img->SetColor($this->fillcolor[$i & 1]);
$this->img->FilledRectangle($xl,$y1,$xr,$y2);
}
}
 
$this->img->SetColor($this->grid_color);
$this->img->SetLineWeight($this->weight);
 
// Draw grid lines
for($i=0; $i<$nbrgrids; ++$i) {
$y=$aTicksPos[$i];
if( $this->type == "solid" )
$this->img->Line($xl,$y,$xr,$y);
elseif( $this->type == "dotted" )
$this->img->DashedLine($xl,$y,$xr,$y,1,6);
elseif( $this->type == "dashed" )
$this->img->DashedLine($xl,$y,$xr,$y,2,4);
elseif( $this->type == "longdashed" )
$this->img->DashedLine($xl,$y,$xr,$y,8,6);
}
}
elseif( $this->scale->type=="x" ) {
$yu=$this->img->top_margin;
$yl=$this->img->height-$this->img->bottom_margin;
$limit=$this->img->width-$this->img->right_margin;
 
if( $this->fill ) {
// Draw filled areas
$x2 = $aTicksPos[0];
$i=1;
while( $i < $nbrgrids ) {
$x1 = $x2;
$x2 = min($aTicksPos[$i++],$limit) ;
$this->img->SetColor($this->fillcolor[$i & 1]);
$this->img->FilledRectangle($x1,$yu,$x2,$yl);
}
}
 
$this->img->SetColor($this->grid_color);
$this->img->SetLineWeight($this->weight);
 
// We must also test for limit since we might have
// an offset and the number of ticks is calculated with
// assumption offset==0 so we might end up drawing one
// to many gridlines
$i=0;
$x=$aTicksPos[$i];
while( $i<count($aTicksPos) && ($x=$aTicksPos[$i]) <= $limit ) {
if( $this->type == "solid" )
$this->img->Line($x,$yl,$x,$yu);
elseif( $this->type == "dotted" )
$this->img->DashedLine($x,$yl,$x,$yu,1,6);
elseif( $this->type == "dashed" )
$this->img->DashedLine($x,$yl,$x,$yu,2,4);
elseif( $this->type == "longdashed" )
$this->img->DashedLine($x,$yl,$x,$yu,8,6);
++$i;
}
}
else {
JpGraphError::Raise('Internal error: Unknown grid axis ['.$this->scale->type.']');
}
return true;
}
} // Class
 
//===================================================
// CLASS Axis
// Description: Defines X and Y axis. Notes that at the
// moment the code is not really good since the axis on
// several occasion must know wheter it's an X or Y axis.
// This was a design decision to make the code easier to
// follow.
//===================================================
class Axis {
var $pos = false;
var $weight=1;
var $color=array(0,0,0),$label_color=array(0,0,0);
var $img=null,$scale=null;
var $hide=false;
var $ticks_label=false, $ticks_label_colors=null;
var $show_first_label=true,$show_last_label=true;
var $label_step=1; // Used by a text axis to specify what multiple of major steps
// should be labeled.
var $tick_step=1;
var $labelPos=0; // Which side of the axis should the labels be?
var $title=null,$title_adjust,$title_margin,$title_side=SIDE_LEFT;
var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$label_angle=0;
var $tick_label_margin=5;
var $label_halign = '',$label_valign = '', $label_para_align='left';
var $hide_line=false,$hide_labels=false;
//var $hide_zero_label=false;
 
//---------------
// CONSTRUCTOR
function Axis(&$img,&$aScale,$color=array(0,0,0)) {
$this->img = &$img;
$this->scale = &$aScale;
$this->color = $color;
$this->title=new Text("");
if( $aScale->type=="y" ) {
$this->title_margin = 25;
$this->title_adjust="middle";
$this->title->SetOrientation(90);
$this->tick_label_margin=7;
$this->labelPos=SIDE_LEFT;
//$this->SetLabelFormat('%.1f');
}
else {
$this->title_margin = 5;
$this->title_adjust="high";
$this->title->SetOrientation(0);
$this->tick_label_margin=3;
$this->labelPos=SIDE_DOWN;
//$this->SetLabelFormat('%.0f');
}
}
//---------------
// PUBLIC METHODS
function SetLabelFormat($aFormStr) {
$this->scale->ticks->SetLabelFormat($aFormStr);
}
function SetLabelFormatString($aFormStr) {
$this->scale->ticks->SetLabelFormat($aFormStr);
}
function SetLabelFormatCallback($aFuncName) {
$this->scale->ticks->SetFormatCallback($aFuncName);
}
 
function SetLabelAlign($aHAlign,$aVAlign="top",$aParagraphAlign='left') {
$this->label_halign = $aHAlign;
$this->label_valign = $aVAlign;
$this->label_para_align = $aParagraphAlign;
}
 
// Don't display the first label
function HideFirstTickLabel($aShow=false) {
$this->show_first_label=$aShow;
}
 
function HideLastTickLabel($aShow=false) {
$this->show_last_label=$aShow;
}
 
function HideTicks($aHideMinor=true,$aHideMajor=true) {
$this->scale->ticks->SupressMinorTickMarks($aHideMinor);
$this->scale->ticks->SupressTickMarks($aHideMajor);
}
 
// Hide zero label
function HideZeroLabel($aFlag=true) {
$this->scale->ticks->SupressZeroLabel();
//$this->hide_zero_label = $aFlag;
}
function HideFirstLastLabel() {
// The two first calls to ticks method will supress
// automatically generated scale values. However, that
// will not affect manually specified value, e.g text-scales.
// therefor we also make a kludge here to supress manually
// specified scale labels.
$this->scale->ticks->SupressLast();
$this->scale->ticks->SupressFirst();
$this->show_first_label = false;
$this->show_last_label = false;
}
// Hide the axis
function Hide($aHide=true) {
$this->hide=$aHide;
}
 
// Hide the actual axis-line, but still print the labels
function HideLine($aHide=true) {
$this->hide_line = $aHide;
}
 
function HideLabels($aHide=true) {
$this->hide_labels = $aHide;
}
 
// Weight of axis
function SetWeight($aWeight) {
$this->weight = $aWeight;
}
 
// Axis color
function SetColor($aColor,$aLabelColor=false) {
$this->color = $aColor;
if( !$aLabelColor ) $this->label_color = $aColor;
else $this->label_color = $aLabelColor;
}
// Title on axis
function SetTitle($aTitle,$aAdjustAlign="high") {
$this->title->Set($aTitle);
$this->title_adjust=$aAdjustAlign;
}
// Specify distance from the axis
function SetTitleMargin($aMargin) {
$this->title_margin=$aMargin;
}
// Which side of the axis should the axis title be?
function SetTitleSide($aSideOfAxis) {
$this->title_side = $aSideOfAxis;
}
 
// Utility function to set the direction for tick marks
function SetTickDirection($aDir) {
// Will be deprecated from 1.7
if( ERR_DEPRECATED )
JpGraphError::Raise('Axis::SetTickDirection() is deprecated. Use Axis::SetTickSide() instead');
$this->scale->ticks->SetSide($aDir);
}
function SetTickSide($aDir) {
$this->scale->ticks->SetSide($aDir);
}
// Specify text labels for the ticks. One label for each data point
function SetTickLabels($aLabelArray,$aLabelColorArray=null) {
$this->ticks_label = $aLabelArray;
$this->ticks_label_colors = $aLabelColorArray;
}
// How far from the axis should the labels be drawn
function SetTickLabelMargin($aMargin) {
if( ERR_DEPRECATED )
JpGraphError::Raise('SetTickLabelMargin() is deprecated. Use Axis::SetLabelMargin() instead.');
$this->tick_label_margin=$aMargin;
}
 
function SetLabelMargin($aMargin) {
$this->tick_label_margin=$aMargin;
}
// Specify that every $step of the ticks should be displayed starting
// at $start
// DEPRECATED FUNCTION: USE SetTextTickInterval() INSTEAD
function SetTextTicks($step,$start=0) {
JpGraphError::Raise(" SetTextTicks() is deprecated. Use SetTextTickInterval() instead.");
}
 
// Specify that every $step of the ticks should be displayed starting
// at $start
function SetTextTickInterval($aStep,$aStart=0) {
$this->scale->ticks->SetTextLabelStart($aStart);
$this->tick_step=$aStep;
}
// Specify that every $step tick mark should have a label
// should be displayed starting
function SetTextLabelInterval($aStep) {
if( $aStep < 1 )
JpGraphError::Raise(" Text label interval must be specified >= 1.");
$this->label_step=$aStep;
}
// Which side of the axis should the labels be on?
function SetLabelPos($aSidePos) {
// This will be deprecated from 1.7
if( ERR_DEPRECATED )
JpGraphError::Raise('SetLabelPos() is deprecated. Use Axis::SetLabelSide() instead.');
$this->labelPos=$aSidePos;
}
function SetLabelSide($aSidePos) {
$this->labelPos=$aSidePos;
}
 
// Set the font
function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
$this->font_family = $aFamily;
$this->font_style = $aStyle;
$this->font_size = $aSize;
}
 
// Position for axis line on the "other" scale
function SetPos($aPosOnOtherScale) {
$this->pos=$aPosOnOtherScale;
}
// Specify the angle for the tick labels
function SetLabelAngle($aAngle) {
$this->label_angle = $aAngle;
}
// Stroke the axis.
function Stroke($aOtherAxisScale) {
if( $this->hide ) return;
if( is_numeric($this->pos) ) {
$pos=$aOtherAxisScale->Translate($this->pos);
}
else { // Default to minimum of other scale if pos not set
if( ($aOtherAxisScale->GetMinVal() >= 0 && $this->pos==false) || $this->pos=="min" ) {
$pos = $aOtherAxisScale->scale_abs[0];
}
elseif($this->pos == "max") {
$pos = $aOtherAxisScale->scale_abs[1];
}
else { // If negative set x-axis at 0
$this->pos=0;
$pos=$aOtherAxisScale->Translate(0);
}
}
$this->img->SetLineWeight($this->weight);
$this->img->SetColor($this->color);
$this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
if( $this->scale->type == "x" ) {
if( !$this->hide_line )
$this->img->FilledRectangle($this->img->left_margin,$pos,
$this->img->width-$this->img->right_margin,$pos+$this->weight-1);
$y=$pos+$this->img->GetFontHeight()+$this->title_margin+$this->title->margin;
if( $this->title_adjust=="high" )
$this->title->Pos($this->img->width-$this->img->right_margin,$y,"right","top");
elseif( $this->title_adjust=="middle" || $this->title_adjust=="center" )
$this->title->Pos(($this->img->width-$this->img->left_margin-$this->img->right_margin)/2+$this->img->left_margin,$y,"center","top");
elseif($this->title_adjust=="low")
$this->title->Pos($this->img->left_margin,$y,"left","top");
else {
JpGraphError::Raise('Unknown alignment specified for X-axis title. ('.$this->title_adjust.')');
}
}
elseif( $this->scale->type == "y" ) {
// Add line weight to the height of the axis since
// the x-axis could have a width>1 and we want the axis to fit nicely together.
if( !$this->hide_line )
$this->img->FilledRectangle($pos-$this->weight+1,$this->img->top_margin,
$pos,$this->img->height-$this->img->bottom_margin+$this->weight-1);
$x=$pos ;
if( $this->title_side == SIDE_LEFT ) {
$x -= $this->title_margin;
$x -= $this->title->margin;
$halign="right";
}
else {
$x += $this->title_margin;
$x += $this->title->margin;
$halign="left";
}
// If the user has manually specified an hor. align
// then we override the automatic settings with this
// specifed setting. Since default is 'left' we compare
// with that. (This means a manually set 'left' align
// will have no effect.)
if( $this->title->halign != 'left' )
$halign = $this->title->halign;
if( $this->title_adjust=="high" )
$this->title->Pos($x,$this->img->top_margin,$halign,"top");
elseif($this->title_adjust=="middle" || $this->title_adjust=="center")
$this->title->Pos($x,($this->img->height-$this->img->top_margin-$this->img->bottom_margin)/2+$this->img->top_margin,$halign,"center");
elseif($this->title_adjust=="low")
$this->title->Pos($x,$this->img->height-$this->img->bottom_margin,$halign,"bottom");
else
JpGraphError::Raise('Unknown alignment specified for Y-axis title. ('.$this->title_adjust.')');
}
$this->scale->ticks->Stroke($this->img,$this->scale,$pos);
if( !$this->hide_labels ) {
$this->StrokeLabels($pos);
}
$this->title->Stroke($this->img);
}
 
//---------------
// PRIVATE METHODS
// Draw all the tick labels on major tick marks
function StrokeLabels($aPos,$aMinor=false,$aAbsLabel=false) {
 
$this->img->SetColor($this->label_color);
$this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
$yoff=$this->img->GetFontHeight()/2;
 
// Only draw labels at major tick marks
$nbr = count($this->scale->ticks->maj_ticks_label);
 
// We have the option to not-display the very first mark
// (Usefull when the first label might interfere with another
// axis.)
$i = $this->show_first_label ? 0 : 1 ;
if( !$this->show_last_label ) --$nbr;
// Now run through all labels making sure we don't overshoot the end
// of the scale.
$ncolor=0;
if( isset($this->ticks_label_colors) )
$ncolor=count($this->ticks_label_colors);
while( $i<$nbr ) {
// $tpos holds the absolute text position for the label
$tpos=$this->scale->ticks->maj_ticklabels_pos[$i];
 
// Note. the $limit is only used for the x axis since we
// might otherwise overshoot if the scale has been centered
// This is due to us "loosing" the last tick mark if we center.
if( $this->scale->type=="x" && $tpos > $this->img->width-$this->img->right_margin+1 ) {
return;
}
// we only draw every $label_step label
if( ($i % $this->label_step)==0 ) {
 
// Set specific label color if specified
if( $ncolor > 0 )
$this->img->SetColor($this->ticks_label_colors[$i % $ncolor]);
// If the label has been specified use that and in other case
// just label the mark with the actual scale value
$m=$this->scale->ticks->GetMajor();
// ticks_label has an entry for each data point and is the array
// that holds the labels set by the user. If the user hasn't
// specified any values we use whats in the automatically asigned
// labels in the maj_ticks_label
if( isset($this->ticks_label[$i*$m]) )
$label=$this->ticks_label[$i*$m];
else {
if( $aAbsLabel )
$label=abs($this->scale->ticks->maj_ticks_label[$i]);
else
$label=$this->scale->ticks->maj_ticks_label[$i];
if( $this->scale->textscale ) {
++$label;
}
}
//if( $this->hide_zero_label && $label==0.0 ) {
// ++$i;
// continue;
//}
if( $this->scale->type == "x" ) {
if( $this->labelPos == SIDE_DOWN ) {
if( $this->label_angle==0 || $this->label_angle==90 ) {
if( $this->label_halign=='' && $this->label_valign=='')
$this->img->SetTextAlign('center','top');
else
$this->img->SetTextAlign($this->label_halign,$this->label_valign);
}
else {
if( $this->label_halign=='' && $this->label_valign=='')
$this->img->SetTextAlign("right","top");
else
$this->img->SetTextAlign($this->label_halign,$this->label_valign);
}
 
$this->img->StrokeText($tpos,$aPos+$this->tick_label_margin,$label,
$this->label_angle,$this->label_para_align);
}
else {
if( $this->label_angle==0 || $this->label_angle==90 ) {
if( $this->label_halign=='' && $this->label_valign=='')
$this->img->SetTextAlign("center","bottom");
else
$this->img->SetTextAlign($this->label_halign,$this->label_valign);
}
else {
if( $this->label_halign=='' && $this->label_valign=='')
$this->img->SetTextAlign("right","bottom");
else
$this->img->SetTextAlign($this->label_halign,$this->label_valign);
}
$this->img->StrokeText($tpos,$aPos-$this->tick_label_margin,$label,
$this->label_angle,$this->label_para_align);
}
}
else {
// scale->type == "y"
//if( $this->label_angle!=0 )
//JpGraphError::Raise(" Labels at an angle are not supported on Y-axis");
if( $this->labelPos == SIDE_LEFT ) { // To the left of y-axis
if( $this->label_halign=='' && $this->label_valign=='')
$this->img->SetTextAlign("right","center");
else
$this->img->SetTextAlign($this->label_halign,$this->label_valign);
$this->img->StrokeText($aPos-$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
}
else { // To the right of the y-axis
if( $this->label_halign=='' && $this->label_valign=='')
$this->img->SetTextAlign("left","center");
else
$this->img->SetTextAlign($this->label_halign,$this->label_valign);
$this->img->StrokeText($aPos+$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
}
}
}
++$i;
}
}
 
} // Class
 
//===================================================
// CLASS Ticks
// Description: Abstract base class for drawing linear and logarithmic
// tick marks on axis
//===================================================
class Ticks {
var $minor_abs_size=3, $major_abs_size=5;
var $direction=1; // Should ticks be in(=1) the plot area or outside (=-1)?
var $scale;
var $is_set=false;
var $precision;
var $supress_zerolabel=false,$supress_first=false;
var $supress_last=false,$supress_tickmarks=false,$supress_minor_tickmarks=false;
var $mincolor="",$majcolor="";
var $weight=1;
var $label_formatstr=''; // C-style format string to use for labels
var $label_formfunc='';
 
 
//---------------
// CONSTRUCTOR
function Ticks(&$aScale) {
$this->scale=&$aScale;
$this->precision = -1;
}
 
//---------------
// PUBLIC METHODS
// Set format string for automatic labels
function SetLabelFormat($aFormatString) {
$this->label_formatstr=$aFormatString;
}
function SetFormatCallback($aCallbackFuncName) {
$this->label_formfunc = $aCallbackFuncName;
}
// Don't display the first zero label
function SupressZeroLabel($aFlag=true) {
$this->supress_zerolabel=$aFlag;
}
// Don't display minor tick marks
function SupressMinorTickMarks($aHide=true) {
$this->supress_minor_tickmarks=$aHide;
}
// Don't display major tick marks
function SupressTickMarks($aHide=true) {
$this->supress_tickmarks=$aHide;
}
// Hide the first tick mark
function SupressFirst($aHide=true) {
$this->supress_first=$aHide;
}
// Hide the last tick mark
function SupressLast($aHide=true) {
$this->supress_last=$aHide;
}
 
// Size (in pixels) of minor tick marks
function GetMinTickAbsSize() {
return $this->minor_abs_size;
}
// Size (in pixels) of major tick marks
function GetMajTickAbsSize() {
return $this->major_abs_size;
}
function SetSize($aMajSize,$aMinSize=3) {
$this->major_abs_size = $aMajSize;
$this->minor_abs_size = $aMinSize;
}
 
// Have the ticks been specified
function IsSpecified() {
return $this->is_set;
}
// Set the distance between major and minor tick marks
function Set($aMaj,$aMin) {
// "Virtual method"
// Should be implemented by the concrete subclass
// if any action is wanted.
}
// Specify number of decimals in automatic labels
// Deprecated from 1.4. Use SetFormatString() instead
function SetPrecision($aPrecision) {
if( ERR_DEPRECATED )
JpGraphError::Raise('Ticks::SetPrecision() is deprecated. Use Ticks::SetLabelFormat() (or Ticks::SetFormatCallback()) instead');
$this->precision=$aPrecision;
}
 
function SetSide($aSide) {
$this->direction=$aSide;
}
// Which side of the axis should the ticks be on
function SetDirection($aSide=SIDE_RIGHT) {
$this->direction=$aSide;
}
// Set colors for major and minor tick marks
function SetMarkColor($aMajorColor,$aMinorColor="") {
$this->SetColor($aMajorColor,$aMinorColor);
}
function SetColor($aMajorColor,$aMinorColor="") {
$this->majcolor=$aMajorColor;
// If not specified use same as major
if( $aMinorColor=="" )
$this->mincolor=$aMajorColor;
else
$this->mincolor=$aMinorColor;
}
function SetWeight($aWeight) {
$this->weight=$aWeight;
}
} // Class
 
//===================================================
// CLASS LinearTicks
// Description: Draw linear ticks on axis
//===================================================
class LinearTicks extends Ticks {
var $minor_step=1, $major_step=2;
var $xlabel_offset=0,$xtick_offset=0;
var $label_offset=0; // What offset should the displayed label have
// i.e should we display 0,1,2 or 1,2,3,4 or 2,3,4 etc
var $text_label_start=0;
//---------------
// CONSTRUCTOR
function LinearTicks() {
$this->precision = -1;
}
 
//---------------
// PUBLIC METHODS
// Return major step size in world coordinates
function GetMajor() {
return $this->major_step;
}
// Return minor step size in world coordinates
function GetMinor() {
return $this->minor_step;
}
// Set Minor and Major ticks (in world coordinates)
function Set($aMajStep,$aMinStep=false) {
if( $aMinStep==false )
$aMinStep=$aMajStep;
if( $aMajStep <= 0 || $aMinStep <= 0 ) {
JpGraphError::Raise(" Minor or major step size is 0. Check that you haven't
got an accidental SetTextTicks(0) in your code.<p>
If this is not the case you might have stumbled upon a bug in JpGraph.
Please report this and if possible include the data that caused the
problem.");
}
$this->major_step=$aMajStep;
$this->minor_step=$aMinStep;
$this->is_set = true;
}
 
// Draw linear ticks
function Stroke(&$img,&$scale,$pos) {
$maj_step_abs = $scale->scale_factor*$this->major_step;
$min_step_abs = $scale->scale_factor*$this->minor_step;
 
if( $min_step_abs==0 || $maj_step_abs==0 )
JpGraphError::Raise(" A plot has an illegal scale. This could for example be that you are trying to use text autoscaling to draw a line plot with only one point or that the plot area is too small. Try increasing the graph size or correct the lineplot.");
$limit = $scale->scale_abs[1];
$nbrmajticks=floor(1.000001*(($scale->GetMaxVal()-$scale->GetMinVal())/$this->major_step))+1;
$first=0;
// If precision hasn't been specified set it to a sensible value
if( $this->precision==-1 ) {
$t = log10($this->minor_step);
if( $t > 0 )
$precision = 0;
else
$precision = -floor($t);
}
else
$precision = $this->precision;
$img->SetLineWeight($this->weight);
// Handle ticks on X-axis
if( $scale->type == "x" ) {
 
// Draw the minor tick marks
$yu = $pos - $this->direction*$this->GetMinTickAbsSize();
$label = $scale->GetMinVal();
$x=$scale->scale_abs[0];
$i=0;
$j=0;
$step = round($maj_step_abs/$min_step_abs);
while( $x < $limit ) {
$this->ticks_pos[]=$x;
$this->ticks_label[]=$label;
$label+=$this->minor_step;
if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
$img->Line($x,$pos,$x,$yu);
if( $this->mincolor!="" ) $img->PopColor();
}
 
if( $i % $step == 0 ) {
$this->maj_ticks_pos[$j]=round($x);//$xtick;
++$j;
}
 
++$i;
$x += $min_step_abs;
}
$this->maj_ticks_pos[$j]=$x;
 
// Draw the major tick marks
$yu = $pos - $this->direction*$this->GetMajTickAbsSize();
// TODO: Add logic to set label_offset for text labels
$label = (float)$scale->GetMinVal()+$this->text_label_start+$this->label_offset;
$start_abs=$scale->scale_factor*$this->text_label_start;
$nbrmajticks=ceil(($scale->GetMaxVal()-$scale->GetMinVal()-$this->text_label_start )/$this->major_step)+1;
$x = $scale->scale_abs[0]+$start_abs+$this->xlabel_offset*$min_step_abs;
for( $i=0; $label<=$scale->GetMaxVal()+$this->label_offset; ++$i ) {
 
// Apply format
if( $this->label_formfunc != "" ) {
$f=$this->label_formfunc;
$l = $f($label);
}
elseif( $this->label_formatstr != "" )
$l = sprintf($this->label_formatstr,$label);
else {
$v = round($label,$precision);
$l = sprintf("%01.".$precision."f",$v);
}
if( ($this->supress_zerolabel && $l==0) ||
($this->supress_first && $i==0) ||
($this->supress_last && $i==$nbrmajticks-1) ) {
$l="";
}
 
$this->maj_ticks_label[$i]=$l;
$label+=$this->major_step;
$this->maj_ticklabels_pos[$i] = $x;
// $this->maj_ticklabels_pos[$i] = $this->maj_ticks_pos[$i];
 
// The x-position of the tick marks can be different from the labels.
// Note that we record the tick position (not the label) so that the grid
// happen upon tick marks and not labels.
$xtick=$scale->scale_abs[0]+$start_abs+$this->xtick_offset*$min_step_abs+$i*$maj_step_abs;
$this->maj_ticks_pos[$i]=$xtick;
if(!($this->xtick_offset > 0 && $i==$nbrmajticks-1) &&
!$this->supress_tickmarks) {
if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
$img->Line($this->maj_ticks_pos[$i],$pos,$this->maj_ticks_pos[$i],$yu);
if( $this->majcolor!="" ) $img->PopColor();
}
 
$x += $maj_step_abs;
}
 
 
}
elseif( $scale->type == "y" ) {
 
// Draw the major tick marks
$xr = $pos + $this->direction*$this->GetMajTickAbsSize();
$label = $scale->GetMinVal();
$tmpmaj=array();
$tmpmin=array();
 
for( $i=0; $i<$nbrmajticks; ++$i) {
$y=$scale->scale_abs[0]+$i*$maj_step_abs;
 
$tmpmaj[]=$y;
 
// THe following two lines might seem to be unecessary but they are not!
// The reason being that for X-axis we separate the position of the labels
// and the tick marks which we don't do for the Y-axis.
// We therefore need to make sure both arrays are corcectly filled
// since Axis::StrokeLabels() uses the label positions and Grid::Stroke() uses
// the tick positions.
$this->maj_ticklabels_pos[$i]=$y;
$this->maj_ticks_pos[$i]=$y;
if( $this->label_formfunc != "" ) {
$f=$this->label_formfunc;
$l = $f($label);
}
elseif( $this->label_formatstr != "" )
$l = sprintf($this->label_formatstr,$label);
else
$l = sprintf("%01.".$precision."f",round($label,$precision));
if( ($this->supress_zerolabel && ($l + 0)==0) || ($this->supress_first && $i==0) ||
($this->supress_last && $i==$nbrmajticks-1) ) {
$l="";
}
$this->maj_ticks_label[$i]=$l;
$label+=$this->major_step;
if( !$this->supress_tickmarks ) {
if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
$img->Line($pos,$y,$xr,$y);
if( $this->majcolor!="" ) $img->PopColor();
}
}
 
// Draw the minor tick marks
$xr = $pos + $this->direction*$this->GetMinTickAbsSize();
$label = $scale->GetMinVal();
for( $i=0,$y=$scale->scale_abs[0]; $y>=$limit; ) {
 
$tmpmin[]=$y;
 
$this->ticks_pos[$i]=$y;
$this->ticks_label[$i]=$label;
$label+=$this->minor_step;
if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
$img->Line($pos,$y,$xr,$y);
if( $this->mincolor!="" ) $img->PopColor();
}
++$i;
$y=$scale->scale_abs[0]+$i*$min_step_abs;
}
}
}
//---------------
// PRIVATE METHODS
// Spoecify the offset of the displayed tick mark with the tick "space"
// Legal values for $o is [0,1] used to adjust where the tick marks and label
// should be positioned within the major tick-size
// $lo specifies the label offset and $to specifies the tick offset
// this comes in handy for example in bar graphs where we wont no offset for the
// tick but have the labels displayed halfway under the bars.
function SetXLabelOffset($aLabelOff,$aTickOff=-1) {
$this->xlabel_offset=$aLabelOff;
if( $aTickOff==-1 ) // Same as label offset
$this->xtick_offset=$aLabelOff;
else
$this->xtick_offset=$aTickOff;
if( $aLabelOff>0 )
$this->SupressLast(); // The last tick wont fit
}
 
// Which tick label should we start with?
function SetTextLabelStart($aTextLabelOff) {
$this->text_label_start=$aTextLabelOff;
}
} // Class
 
//===================================================
// CLASS LinearScale
// Description: Handle linear scaling between screen and world
//===================================================
class LinearScale {
var $scale=array(0,0);
var $scale_abs=array(0,0);
var $scale_factor; // Scale factor between world and screen
var $world_size; // Plot area size in world coordinates
var $world_abs_size; // Plot area size in pixels
var $off; // Offset between image edge and plot area
var $type; // is this x or y scale ?
var $ticks=null; // Store ticks
var $autoscale_min=false; // Forced minimum value, auto determine max
var $autoscale_max=false; // Forced maximum value, auto determine min
var $gracetop=0,$gracebottom=0;
var $intscale=false; // Restrict autoscale to integers
var $textscale=false; // Just a flag to let the Plot class find out if
// we are a textscale or not. This is a cludge since
// this ionformatyion is availabale in Graph::axtype but
// we don't have access to the graph object in the Plots
// stroke method. So we let graph store the status here
// when the linear scale is created. A real cludge...
var $text_scale_off = 0;
var $auto_ticks=false; // When using manual scale should the ticks be automatically set?
var $name = 'lin';
//---------------
// CONSTRUCTOR
function LinearScale($aMin=0,$aMax=0,$aType="y") {
assert($aType=="x" || $aType=="y" );
assert($aMin<=$aMax);
$this->type=$aType;
$this->scale=array($aMin,$aMax);
$this->world_size=$aMax-$aMin;
$this->ticks = new LinearTicks();
}
 
//---------------
// PUBLIC METHODS
// Second phase constructor
function Init(&$aImg) {
$this->InitConstants($aImg);
// We want image to notify us when the margins changes so we
// can recalculate the constants.
// PHP <= 4.04 BUGWARNING: IT IS IMPOSSIBLE TO DO THIS IN THE CONSTRUCTOR
// SINCE (FOR SOME REASON) IT IS IMPOSSIBLE TO PASS A REFERENCE
// TO 'this' INSTEAD IT WILL ADD AN ANONYMOUS COPY OF THIS OBJECT WHICH WILL
// GET ALL THE NOTIFICATIONS. (This took a while to track down...)
// Add us as an observer to class Image
$aImg->AddObserver("InitConstants",$this);
}
// Check if scale is set or if we should autoscale
// We should do this is either scale or ticks has not been set
function IsSpecified() {
if( $this->GetMinVal()==$this->GetMaxVal() ) { // Scale not set
return false;
}
return true;
}
// Set the minimum data value when the autoscaling is used.
// Usefull if you want a fix minimum (like 0) but have an
// automatic maximum
function SetAutoMin($aMin) {
$this->autoscale_min=$aMin;
}
 
// Set the minimum data value when the autoscaling is used.
// Usefull if you want a fix minimum (like 0) but have an
// automatic maximum
function SetAutoMax($aMax) {
$this->autoscale_max=$aMax;
}
 
// If the user manually specifies a scale should the ticks
// still be set automatically?
function SetAutoTicks($aFlag=true) {
$this->auto_ticks = $aFlag;
}
 
// Specify scale "grace" value (top and bottom)
function SetGrace($aGraceTop,$aGraceBottom=0) {
if( $aGraceTop<0 || $aGraceBottom < 0 )
JpGraphError::Raise(" Grace must be larger then 0");
$this->gracetop=$aGraceTop;
$this->gracebottom=$aGraceBottom;
}
// Get the minimum value in the scale
function GetMinVal() {
return $this->scale[0];
}
// get maximum value for scale
function GetMaxVal() {
return $this->scale[1];
}
// Specify a new min/max value for sclae
function Update(&$aImg,$aMin,$aMax) {
$this->scale=array($aMin,$aMax);
$this->world_size=$aMax-$aMin;
$this->InitConstants($aImg);
}
// Translate between world and screen
function Translate($aCoord) {
return $this->off+($aCoord - $this->GetMinVal()) * $this->scale_factor;
}
// Relative translate (don't include offset) usefull when we just want
// to know the relative position (in pixels) on the axis
function RelTranslate($aCoord) {
return ($aCoord - $this->GetMinVal()) * $this->scale_factor;
}
// Restrict autoscaling to only use integers
function SetIntScale($aIntScale=true) {
$this->intscale=$aIntScale;
}
// Calculate an integer autoscale
function IntAutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
// Make sure limits are integers
$min=floor($min);
$max=ceil($max);
if( abs($min-$max)==0 ) {
--$min; ++$max;
}
$maxsteps = floor($maxsteps);
$gracetop=round(($this->gracetop/100.0)*abs($max-$min));
$gracebottom=round(($this->gracebottom/100.0)*abs($max-$min));
if( is_numeric($this->autoscale_min) ) {
$min = ceil($this->autoscale_min);
if( $min >= $max ) {
JpGraphError::Raise('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
die();
}
}
 
if( is_numeric($this->autoscale_max) ) {
$max = ceil($this->autoscale_max);
if( $min >= $max ) {
JpGraphError::Raise('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
die();
}
}
 
if( abs($min-$max ) == 0 ) {
++$max;
--$min;
}
$min -= $gracebottom;
$max += $gracetop;
 
// First get tickmarks as multiples of 1, 10, ...
if( $majend ) {
list($num1steps,$adj1min,$adj1max,$maj1step) =
$this->IntCalcTicks($maxsteps,$min,$max,1);
}
else {
$adj1min = $min;
$adj1max = $max;
list($num1steps,$maj1step) =
$this->IntCalcTicksFreeze($maxsteps,$min,$max,1);
}
 
if( abs($min-$max) > 2 ) {
// Then get tick marks as 2:s 2, 20, ...
if( $majend ) {
list($num2steps,$adj2min,$adj2max,$maj2step) =
$this->IntCalcTicks($maxsteps,$min,$max,5);
}
else {
$adj2min = $min;
$adj2max = $max;
list($num2steps,$maj2step) =
$this->IntCalcTicksFreeze($maxsteps,$min,$max,5);
}
}
else {
$num2steps = 10000; // Dummy high value so we don't choose this
}
if( abs($min-$max) > 5 ) {
// Then get tickmarks as 5:s 5, 50, 500, ...
if( $majend ) {
list($num5steps,$adj5min,$adj5max,$maj5step) =
$this->IntCalcTicks($maxsteps,$min,$max,2);
}
else {
$adj5min = $min;
$adj5max = $max;
list($num5steps,$maj5step) =
$this->IntCalcTicksFreeze($maxsteps,$min,$max,2);
}
}
else {
$num5steps = 10000; // Dummy high value so we don't choose this
}
// Check to see whichof 1:s, 2:s or 5:s fit better with
// the requested number of major ticks
$match1=abs($num1steps-$maxsteps);
$match2=abs($num2steps-$maxsteps);
if( !empty($maj5step) && $maj5step > 1 )
$match5=abs($num5steps-$maxsteps);
else
$match5=10000; // Dummy high value
// Compare these three values and see which is the closest match
// We use a 0.6 weight to gravitate towards multiple of 5:s
if( $match1 < $match2 ) {
if( $match1 < $match5 )
$r=1;
else
$r=3;
}
else {
if( $match2 < $match5 )
$r=2;
else
$r=3;
}
// Minsteps are always the same as maxsteps for integer scale
switch( $r ) {
case 1:
$this->Update($img,$adj1min,$adj1max);
$this->ticks->Set($maj1step,$maj1step);
break;
case 2:
$this->Update($img,$adj2min,$adj2max);
$this->ticks->Set($maj2step,$maj2step);
break;
case 3:
$this->Update($img,$adj5min,$adj5max);
$this->ticks->Set($maj5step,$maj2step);
break;
}
}
// Calculate autoscale. Used if user hasn't given a scale and ticks
// $maxsteps is the maximum number of major tickmarks allowed.
function AutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
if( $this->intscale ) {
$this->IntAutoScale($img,$min,$max,$maxsteps,$majend);
return;
}
if( abs($min-$max) < 0.00001 ) {
// We need some difference to be able to autoscale
// make it 5% above and 5% below value
if( $min==0 && $max==0 ) { // Special case
$min=-1; $max=1;
}
else {
$delta = (abs($max)+abs($min))*0.005;
$min -= $delta;
$max += $delta;
}
}
$gracetop=($this->gracetop/100.0)*abs($max-$min);
$gracebottom=($this->gracebottom/100.0)*abs($max-$min);
if( is_numeric($this->autoscale_min) ) {
$min = $this->autoscale_min;
if( $min >= $max ) {
JpGraphError::Raise('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
die();
}
if( abs($min-$max ) < 0.00001 )
$max *= 1.2;
}
 
if( is_numeric($this->autoscale_max) ) {
$max = $this->autoscale_max;
if( $min >= $max ) {
JpGraphError::Raise('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
die();
}
if( abs($min-$max ) < 0.00001 )
$min *= 0.8;
}
 
$min -= $gracebottom;
$max += $gracetop;
 
// First get tickmarks as multiples of 0.1, 1, 10, ...
if( $majend ) {
list($num1steps,$adj1min,$adj1max,$min1step,$maj1step) =
$this->CalcTicks($maxsteps,$min,$max,1,2);
}
else {
$adj1min=$min;
$adj1max=$max;
list($num1steps,$min1step,$maj1step) =
$this->CalcTicksFreeze($maxsteps,$min,$max,1,2,false);
}
// Then get tick marks as 2:s 0.2, 2, 20, ...
if( $majend ) {
list($num2steps,$adj2min,$adj2max,$min2step,$maj2step) =
$this->CalcTicks($maxsteps,$min,$max,5,2);
}
else {
$adj2min=$min;
$adj2max=$max;
list($num2steps,$min2step,$maj2step) =
$this->CalcTicksFreeze($maxsteps,$min,$max,5,2,false);
}
// Then get tickmarks as 5:s 0.05, 0.5, 5, 50, ...
if( $majend ) {
list($num5steps,$adj5min,$adj5max,$min5step,$maj5step) =
$this->CalcTicks($maxsteps,$min,$max,2,5);
}
else {
$adj5min=$min;
$adj5max=$max;
list($num5steps,$min5step,$maj5step) =
$this->CalcTicksFreeze($maxsteps,$min,$max,2,5,false);
}
 
// Check to see whichof 1:s, 2:s or 5:s fit better with
// the requested number of major ticks
$match1=abs($num1steps-$maxsteps);
$match2=abs($num2steps-$maxsteps);
$match5=abs($num5steps-$maxsteps);
// Compare these three values and see which is the closest match
// We use a 0.8 weight to gravitate towards multiple of 5:s
$r=$this->MatchMin3($match1,$match2,$match5,0.8);
switch( $r ) {
case 1:
$this->Update($img,$adj1min,$adj1max);
$this->ticks->Set($maj1step,$min1step);
break;
case 2:
$this->Update($img,$adj2min,$adj2max);
$this->ticks->Set($maj2step,$min2step);
break;
case 3:
$this->Update($img,$adj5min,$adj5max);
$this->ticks->Set($maj5step,$min5step);
break;
}
}
 
//---------------
// PRIVATE METHODS
 
// This method recalculates all constants that are depending on the
// margins in the image. If the margins in the image are changed
// this method should be called for every scale that is registred with
// that image. Should really be installed as an observer of that image.
function InitConstants(&$img) {
if( $this->type=="x" ) {
$this->world_abs_size=$img->width - $img->left_margin - $img->right_margin;
$this->off=$img->left_margin;
$this->scale_factor = 0;
if( $this->world_size > 0 )
$this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
}
else { // y scale
$this->world_abs_size=$img->height - $img->top_margin - $img->bottom_margin;
$this->off=$img->top_margin+$this->world_abs_size;
$this->scale_factor = 0;
if( $this->world_size > 0 )
$this->scale_factor=-$this->world_abs_size/($this->world_size*1.0);
}
$size = $this->world_size * $this->scale_factor;
$this->scale_abs=array($this->off,$this->off + $size);
}
// Initialize the conversion constants for this scale
// This tries to pre-calculate as much as possible to speed up the
// actual conversion (with Translate()) later on
// $start =scale start in absolute pixels (for x-scale this is an y-position
// and for an y-scale this is an x-position
// $len =absolute length in pixels of scale
function SetConstants($aStart,$aLen) {
$this->world_abs_size=$aLen;
$this->off=$aStart;
if( $this->world_size<=0 ) {
JpGraphError::Raise("<b>JpGraph Fatal Error</b>:<br>
You have unfortunately stumbled upon a bug in JpGraph. <br>
It seems like the scale range is ".$this->world_size." [for ".
$this->type." scale] <br>
Please report Bug #01 to jpgraph@aditus.nu and include the script
that gave this error. <br>
This problem could potentially be caused by trying to use \"illegal\"
values in the input data arrays (like trying to send in strings or
only NULL values) which causes the autoscaling to fail.");
}
// scale_factor = number of pixels per world unit
$this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
// scale_abs = start and end points of scale in absolute pixels
$this->scale_abs=array($this->off,$this->off+$this->world_size*$this->scale_factor);
}
// Calculate number of ticks steps with a specific division
// $a is the divisor of 10**x to generate the first maj tick intervall
// $a=1, $b=2 give major ticks with multiple of 10, ...,0.1,1,10,...
// $a=5, $b=2 give major ticks with multiple of 2:s ...,0.2,2,20,...
// $a=2, $b=5 give major ticks with multiple of 5:s ...,0.5,5,50,...
// We return a vector of
// [$numsteps,$adjmin,$adjmax,$minstep,$majstep]
// If $majend==true then the first and last marks on the axis will be major
// labeled tick marks otherwise it will be adjusted to the closest min tick mark
function CalcTicks($maxsteps,$min,$max,$a,$b,$majend=true) {
$diff=$max-$min;
if( $diff==0 )
$ld=0;
else
$ld=floor(log10($diff));
 
// Gravitate min towards zero if we are close
if( $min>0 && $min < pow(10,$ld) ) $min=0;
//$majstep=pow(10,$ld-1)/$a;
$majstep=pow(10,$ld)/$a;
$minstep=$majstep/$b;
$adjmax=ceil($max/$minstep)*$minstep;
$adjmin=floor($min/$minstep)*$minstep;
$adjdiff = $adjmax-$adjmin;
$numsteps=$adjdiff/$majstep;
while( $numsteps>$maxsteps ) {
$majstep=pow(10,$ld)/$a;
$numsteps=$adjdiff/$majstep;
++$ld;
}
 
$minstep=$majstep/$b;
$adjmin=floor($min/$minstep)*$minstep;
$adjdiff = $adjmax-$adjmin;
if( $majend ) {
$adjmin = floor($min/$majstep)*$majstep;
$adjdiff = $adjmax-$adjmin;
$adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
}
else
$adjmax=ceil($max/$minstep)*$minstep;
 
return array($numsteps,$adjmin,$adjmax,$minstep,$majstep);
}
 
function CalcTicksFreeze($maxsteps,$min,$max,$a,$b) {
// Same as CalcTicks but don't adjust min/max values
$diff=$max-$min;
if( $diff==0 )
$ld=0;
else
$ld=floor(log10($diff));
 
//$majstep=pow(10,$ld-1)/$a;
$majstep=pow(10,$ld)/$a;
$minstep=$majstep/$b;
$numsteps=floor($diff/$majstep);
while( $numsteps > $maxsteps ) {
$majstep=pow(10,$ld)/$a;
$numsteps=floor($diff/$majstep);
++$ld;
}
$minstep=$majstep/$b;
return array($numsteps,$minstep,$majstep);
}
 
function IntCalcTicks($maxsteps,$min,$max,$a,$majend=true) {
$diff=$max-$min;
if( $diff==0 )
JpGraphError::Raise('Can\'t automatically determine ticks since min==max.');
else
$ld=floor(log10($diff));
// Gravitate min towards zero if we are close
if( $min>0 && $min < pow(10,$ld) ) $min=0;
if( $ld == 0 ) $ld=1;
if( $a == 1 )
$majstep = 1;
else
$majstep=pow(10,$ld)/$a;
$adjmax=ceil($max/$majstep)*$majstep;
 
$adjmin=floor($min/$majstep)*$majstep;
$adjdiff = $adjmax-$adjmin;
$numsteps=$adjdiff/$majstep;
while( $numsteps>$maxsteps ) {
$majstep=pow(10,$ld)/$a;
$numsteps=$adjdiff/$majstep;
++$ld;
}
$adjmin=floor($min/$majstep)*$majstep;
$adjdiff = $adjmax-$adjmin;
if( $majend ) {
$adjmin = floor($min/$majstep)*$majstep;
$adjdiff = $adjmax-$adjmin;
$adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
}
else
$adjmax=ceil($max/$majstep)*$majstep;
return array($numsteps,$adjmin,$adjmax,$majstep);
}
 
 
function IntCalcTicksFreeze($maxsteps,$min,$max,$a) {
// Same as IntCalcTick but don't change min/max values
$diff=$max-$min;
if( $diff==0 )
JpGraphError::Raise('Can\'t automatically determine ticks since min==max.');
else
$ld=floor(log10($diff));
if( $ld == 0 ) $ld=1;
if( $a == 1 )
$majstep = 1;
else
$majstep=pow(10,$ld)/$a;
 
$numsteps=floor($diff/$majstep);
while( $numsteps > $maxsteps ) {
$majstep=pow(10,$ld)/$a;
$numsteps=floor($diff/$majstep);
++$ld;
}
return array($numsteps,$majstep);
}
 
 
// Determine the minimum of three values witha weight for last value
function MatchMin3($a,$b,$c,$weight) {
if( $a < $b ) {
if( $a < ($c*$weight) )
return 1; // $a smallest
else
return 3; // $c smallest
}
elseif( $b < ($c*$weight) )
return 2; // $b smallest
return 3; // $c smallest
}
} // Class
 
//===================================================
// CLASS RGB
// Description: Color definitions as RGB triples
//===================================================
class RGB {
var $rgb_table;
var $img;
function RGB($aImg=null) {
$this->img = $aImg;
// Conversion array between color names and RGB
$this->rgb_table = array(
"aqua"=> array(0,255,255),
"lime"=> array(0,255,0),
"teal"=> array(0,128,128),
"whitesmoke"=>array(245,245,245),
"gainsboro"=>array(220,220,220),
"oldlace"=>array(253,245,230),
"linen"=>array(250,240,230),
"antiquewhite"=>array(250,235,215),
"papayawhip"=>array(255,239,213),
"blanchedalmond"=>array(255,235,205),
"bisque"=>array(255,228,196),
"peachpuff"=>array(255,218,185),
"navajowhite"=>array(255,222,173),
"moccasin"=>array(255,228,181),
"cornsilk"=>array(255,248,220),
"ivory"=>array(255,255,240),
"lemonchiffon"=>array(255,250,205),
"seashell"=>array(255,245,238),
"mintcream"=>array(245,255,250),
"azure"=>array(240,255,255),
"aliceblue"=>array(240,248,255),
"lavender"=>array(230,230,250),
"lavenderblush"=>array(255,240,245),
"mistyrose"=>array(255,228,225),
"white"=>array(255,255,255),
"black"=>array(0,0,0),
"darkslategray"=>array(47,79,79),
"dimgray"=>array(105,105,105),
"slategray"=>array(112,128,144),
"lightslategray"=>array(119,136,153),
"gray"=>array(190,190,190),
"lightgray"=>array(211,211,211),
"midnightblue"=>array(25,25,112),
"navy"=>array(0,0,128),
"cornflowerblue"=>array(100,149,237),
"darkslateblue"=>array(72,61,139),
"slateblue"=>array(106,90,205),
"mediumslateblue"=>array(123,104,238),
"lightslateblue"=>array(132,112,255),
"mediumblue"=>array(0,0,205),
"royalblue"=>array(65,105,225),
"blue"=>array(0,0,255),
"dodgerblue"=>array(30,144,255),
"deepskyblue"=>array(0,191,255),
"skyblue"=>array(135,206,235),
"lightskyblue"=>array(135,206,250),
"steelblue"=>array(70,130,180),
"lightred"=>array(211,167,168),
"lightsteelblue"=>array(176,196,222),
"lightblue"=>array(173,216,230),
"powderblue"=>array(176,224,230),
"paleturquoise"=>array(175,238,238),
"darkturquoise"=>array(0,206,209),
"mediumturquoise"=>array(72,209,204),
"turquoise"=>array(64,224,208),
"cyan"=>array(0,255,255),
"lightcyan"=>array(224,255,255),
"cadetblue"=>array(95,158,160),
"mediumaquamarine"=>array(102,205,170),
"aquamarine"=>array(127,255,212),
"darkgreen"=>array(0,100,0),
"darkolivegreen"=>array(85,107,47),
"darkseagreen"=>array(143,188,143),
"seagreen"=>array(46,139,87),
"mediumseagreen"=>array(60,179,113),
"lightseagreen"=>array(32,178,170),
"palegreen"=>array(152,251,152),
"springgreen"=>array(0,255,127),
"lawngreen"=>array(124,252,0),
"green"=>array(0,255,0),
"chartreuse"=>array(127,255,0),
"mediumspringgreen"=>array(0,250,154),
"greenyellow"=>array(173,255,47),
"limegreen"=>array(50,205,50),
"yellowgreen"=>array(154,205,50),
"forestgreen"=>array(34,139,34),
"olivedrab"=>array(107,142,35),
"darkkhaki"=>array(189,183,107),
"khaki"=>array(240,230,140),
"palegoldenrod"=>array(238,232,170),
"lightgoldenrodyellow"=>array(250,250,210),
"lightyellow"=>array(255,255,200),
"yellow"=>array(255,255,0),
"gold"=>array(255,215,0),
"lightgoldenrod"=>array(238,221,130),
"goldenrod"=>array(218,165,32),
"darkgoldenrod"=>array(184,134,11),
"rosybrown"=>array(188,143,143),
"indianred"=>array(205,92,92),
"saddlebrown"=>array(139,69,19),
"sienna"=>array(160,82,45),
"peru"=>array(205,133,63),
"burlywood"=>array(222,184,135),
"beige"=>array(245,245,220),
"wheat"=>array(245,222,179),
"sandybrown"=>array(244,164,96),
"tan"=>array(210,180,140),
"chocolate"=>array(210,105,30),
"firebrick"=>array(178,34,34),
"brown"=>array(165,42,42),
"darksalmon"=>array(233,150,122),
"salmon"=>array(250,128,114),
"lightsalmon"=>array(255,160,122),
"orange"=>array(255,165,0),
"darkorange"=>array(255,140,0),
"coral"=>array(255,127,80),
"lightcoral"=>array(240,128,128),
"tomato"=>array(255,99,71),
"orangered"=>array(255,69,0),
"red"=>array(255,0,0),
"hotpink"=>array(255,105,180),
"deeppink"=>array(255,20,147),
"pink"=>array(255,192,203),
"lightpink"=>array(255,182,193),
"palevioletred"=>array(219,112,147),
"maroon"=>array(176,48,96),
"mediumvioletred"=>array(199,21,133),
"violetred"=>array(208,32,144),
"magenta"=>array(255,0,255),
"violet"=>array(238,130,238),
"plum"=>array(221,160,221),
"orchid"=>array(218,112,214),
"mediumorchid"=>array(186,85,211),
"darkorchid"=>array(153,50,204),
"darkviolet"=>array(148,0,211),
"blueviolet"=>array(138,43,226),
"purple"=>array(160,32,240),
"mediumpurple"=>array(147,112,219),
"thistle"=>array(216,191,216),
"snow1"=>array(255,250,250),
"snow2"=>array(238,233,233),
"snow3"=>array(205,201,201),
"snow4"=>array(139,137,137),
"seashell1"=>array(255,245,238),
"seashell2"=>array(238,229,222),
"seashell3"=>array(205,197,191),
"seashell4"=>array(139,134,130),
"AntiqueWhite1"=>array(255,239,219),
"AntiqueWhite2"=>array(238,223,204),
"AntiqueWhite3"=>array(205,192,176),
"AntiqueWhite4"=>array(139,131,120),
"bisque1"=>array(255,228,196),
"bisque2"=>array(238,213,183),
"bisque3"=>array(205,183,158),
"bisque4"=>array(139,125,107),
"peachPuff1"=>array(255,218,185),
"peachpuff2"=>array(238,203,173),
"peachpuff3"=>array(205,175,149),
"peachpuff4"=>array(139,119,101),
"navajowhite1"=>array(255,222,173),
"navajowhite2"=>array(238,207,161),
"navajowhite3"=>array(205,179,139),
"navajowhite4"=>array(139,121,94),
"lemonchiffon1"=>array(255,250,205),
"lemonchiffon2"=>array(238,233,191),
"lemonchiffon3"=>array(205,201,165),
"lemonchiffon4"=>array(139,137,112),
"ivory1"=>array(255,255,240),
"ivory2"=>array(238,238,224),
"ivory3"=>array(205,205,193),
"ivory4"=>array(139,139,131),
"honeydew"=>array(193,205,193),
"lavenderblush1"=>array(255,240,245),
"lavenderblush2"=>array(238,224,229),
"lavenderblush3"=>array(205,193,197),
"lavenderblush4"=>array(139,131,134),
"mistyrose1"=>array(255,228,225),
"mistyrose2"=>array(238,213,210),
"mistyrose3"=>array(205,183,181),
"mistyrose4"=>array(139,125,123),
"azure1"=>array(240,255,255),
"azure2"=>array(224,238,238),
"azure3"=>array(193,205,205),
"azure4"=>array(131,139,139),
"slateblue1"=>array(131,111,255),
"slateblue2"=>array(122,103,238),
"slateblue3"=>array(105,89,205),
"slateblue4"=>array(71,60,139),
"royalblue1"=>array(72,118,255),
"royalblue2"=>array(67,110,238),
"royalblue3"=>array(58,95,205),
"royalblue4"=>array(39,64,139),
"dodgerblue1"=>array(30,144,255),
"dodgerblue2"=>array(28,134,238),
"dodgerblue3"=>array(24,116,205),
"dodgerblue4"=>array(16,78,139),
"steelblue1"=>array(99,184,255),
"steelblue2"=>array(92,172,238),
"steelblue3"=>array(79,148,205),
"steelblue4"=>array(54,100,139),
"deepskyblue1"=>array(0,191,255),
"deepskyblue2"=>array(0,178,238),
"deepskyblue3"=>array(0,154,205),
"deepskyblue4"=>array(0,104,139),
"skyblue1"=>array(135,206,255),
"skyblue2"=>array(126,192,238),
"skyblue3"=>array(108,166,205),
"skyblue4"=>array(74,112,139),
"lightskyblue1"=>array(176,226,255),
"lightskyblue2"=>array(164,211,238),
"lightskyblue3"=>array(141,182,205),
"lightskyblue4"=>array(96,123,139),
"slategray1"=>array(198,226,255),
"slategray2"=>array(185,211,238),
"slategray3"=>array(159,182,205),
"slategray4"=>array(108,123,139),
"lightsteelblue1"=>array(202,225,255),
"lightsteelblue2"=>array(188,210,238),
"lightsteelblue3"=>array(162,181,205),
"lightsteelblue4"=>array(110,123,139),
"lightblue1"=>array(191,239,255),
"lightblue2"=>array(178,223,238),
"lightblue3"=>array(154,192,205),
"lightblue4"=>array(104,131,139),
"lightcyan1"=>array(224,255,255),
"lightcyan2"=>array(209,238,238),
"lightcyan3"=>array(180,205,205),
"lightcyan4"=>array(122,139,139),
"paleturquoise1"=>array(187,255,255),
"paleturquoise2"=>array(174,238,238),
"paleturquoise3"=>array(150,205,205),
"paleturquoise4"=>array(102,139,139),
"cadetblue1"=>array(152,245,255),
"cadetblue2"=>array(142,229,238),
"cadetblue3"=>array(122,197,205),
"cadetblue4"=>array(83,134,139),
"turquoise1"=>array(0,245,255),
"turquoise2"=>array(0,229,238),
"turquoise3"=>array(0,197,205),
"turquoise4"=>array(0,134,139),
"cyan1"=>array(0,255,255),
"cyan2"=>array(0,238,238),
"cyan3"=>array(0,205,205),
"cyan4"=>array(0,139,139),
"darkslategray1"=>array(151,255,255),
"darkslategray2"=>array(141,238,238),
"darkslategray3"=>array(121,205,205),
"darkslategray4"=>array(82,139,139),
"aquamarine1"=>array(127,255,212),
"aquamarine2"=>array(118,238,198),
"aquamarine3"=>array(102,205,170),
"aquamarine4"=>array(69,139,116),
"darkseagreen1"=>array(193,255,193),
"darkseagreen2"=>array(180,238,180),
"darkseagreen3"=>array(155,205,155),
"darkseagreen4"=>array(105,139,105),
"seagreen1"=>array(84,255,159),
"seagreen2"=>array(78,238,148),
"seagreen3"=>array(67,205,128),
"seagreen4"=>array(46,139,87),
"palegreen1"=>array(154,255,154),
"palegreen2"=>array(144,238,144),
"palegreen3"=>array(124,205,124),
"palegreen4"=>array(84,139,84),
"springgreen1"=>array(0,255,127),
"springgreen2"=>array(0,238,118),
"springgreen3"=>array(0,205,102),
"springgreen4"=>array(0,139,69),
"chartreuse1"=>array(127,255,0),
"chartreuse2"=>array(118,238,0),
"chartreuse3"=>array(102,205,0),
"chartreuse4"=>array(69,139,0),
"olivedrab1"=>array(192,255,62),
"olivedrab2"=>array(179,238,58),
"olivedrab3"=>array(154,205,50),
"olivedrab4"=>array(105,139,34),
"darkolivegreen1"=>array(202,255,112),
"darkolivegreen2"=>array(188,238,104),
"darkolivegreen3"=>array(162,205,90),
"darkolivegreen4"=>array(110,139,61),
"khaki1"=>array(255,246,143),
"khaki2"=>array(238,230,133),
"khaki3"=>array(205,198,115),
"khaki4"=>array(139,134,78),
"lightgoldenrod1"=>array(255,236,139),
"lightgoldenrod2"=>array(238,220,130),
"lightgoldenrod3"=>array(205,190,112),
"lightgoldenrod4"=>array(139,129,76),
"yellow1"=>array(255,255,0),
"yellow2"=>array(238,238,0),
"yellow3"=>array(205,205,0),
"yellow4"=>array(139,139,0),
"gold1"=>array(255,215,0),
"gold2"=>array(238,201,0),
"gold3"=>array(205,173,0),
"gold4"=>array(139,117,0),
"goldenrod1"=>array(255,193,37),
"goldenrod2"=>array(238,180,34),
"goldenrod3"=>array(205,155,29),
"goldenrod4"=>array(139,105,20),
"darkgoldenrod1"=>array(255,185,15),
"darkgoldenrod2"=>array(238,173,14),
"darkgoldenrod3"=>array(205,149,12),
"darkgoldenrod4"=>array(139,101,8),
"rosybrown1"=>array(255,193,193),
"rosybrown2"=>array(238,180,180),
"rosybrown3"=>array(205,155,155),
"rosybrown4"=>array(139,105,105),
"indianred1"=>array(255,106,106),
"indianred2"=>array(238,99,99),
"indianred3"=>array(205,85,85),
"indianred4"=>array(139,58,58),
"sienna1"=>array(255,130,71),
"sienna2"=>array(238,121,66),
"sienna3"=>array(205,104,57),
"sienna4"=>array(139,71,38),
"burlywood1"=>array(255,211,155),
"burlywood2"=>array(238,197,145),
"burlywood3"=>array(205,170,125),
"burlywood4"=>array(139,115,85),
"wheat1"=>array(255,231,186),
"wheat2"=>array(238,216,174),
"wheat3"=>array(205,186,150),
"wheat4"=>array(139,126,102),
"tan1"=>array(255,165,79),
"tan2"=>array(238,154,73),
"tan3"=>array(205,133,63),
"tan4"=>array(139,90,43),
"chocolate1"=>array(255,127,36),
"chocolate2"=>array(238,118,33),
"chocolate3"=>array(205,102,29),
"chocolate4"=>array(139,69,19),
"firebrick1"=>array(255,48,48),
"firebrick2"=>array(238,44,44),
"firebrick3"=>array(205,38,38),
"firebrick4"=>array(139,26,26),
"brown1"=>array(255,64,64),
"brown2"=>array(238,59,59),
"brown3"=>array(205,51,51),
"brown4"=>array(139,35,35),
"salmon1"=>array(255,140,105),
"salmon2"=>array(238,130,98),
"salmon3"=>array(205,112,84),
"salmon4"=>array(139,76,57),
"lightsalmon1"=>array(255,160,122),
"lightsalmon2"=>array(238,149,114),
"lightsalmon3"=>array(205,129,98),
"lightsalmon4"=>array(139,87,66),
"orange1"=>array(255,165,0),
"orange2"=>array(238,154,0),
"orange3"=>array(205,133,0),
"orange4"=>array(139,90,0),
"darkorange1"=>array(255,127,0),
"darkorange2"=>array(238,118,0),
"darkorange3"=>array(205,102,0),
"darkorange4"=>array(139,69,0),
"coral1"=>array(255,114,86),
"coral2"=>array(238,106,80),
"coral3"=>array(205,91,69),
"coral4"=>array(139,62,47),
"tomato1"=>array(255,99,71),
"tomato2"=>array(238,92,66),
"tomato3"=>array(205,79,57),
"tomato4"=>array(139,54,38),
"orangered1"=>array(255,69,0),
"orangered2"=>array(238,64,0),
"orangered3"=>array(205,55,0),
"orangered4"=>array(139,37,0),
"deeppink1"=>array(255,20,147),
"deeppink2"=>array(238,18,137),
"deeppink3"=>array(205,16,118),
"deeppink4"=>array(139,10,80),
"hotpink1"=>array(255,110,180),
"hotpink2"=>array(238,106,167),
"hotpink3"=>array(205,96,144),
"hotpink4"=>array(139,58,98),
"pink1"=>array(255,181,197),
"pink2"=>array(238,169,184),
"pink3"=>array(205,145,158),
"pink4"=>array(139,99,108),
"lightpink1"=>array(255,174,185),
"lightpink2"=>array(238,162,173),
"lightpink3"=>array(205,140,149),
"lightpink4"=>array(139,95,101),
"palevioletred1"=>array(255,130,171),
"palevioletred2"=>array(238,121,159),
"palevioletred3"=>array(205,104,137),
"palevioletred4"=>array(139,71,93),
"maroon1"=>array(255,52,179),
"maroon2"=>array(238,48,167),
"maroon3"=>array(205,41,144),
"maroon4"=>array(139,28,98),
"violetred1"=>array(255,62,150),
"violetred2"=>array(238,58,140),
"violetred3"=>array(205,50,120),
"violetred4"=>array(139,34,82),
"magenta1"=>array(255,0,255),
"magenta2"=>array(238,0,238),
"magenta3"=>array(205,0,205),
"magenta4"=>array(139,0,139),
"mediumred"=>array(140,34,34),
"orchid1"=>array(255,131,250),
"orchid2"=>array(238,122,233),
"orchid3"=>array(205,105,201),
"orchid4"=>array(139,71,137),
"plum1"=>array(255,187,255),
"plum2"=>array(238,174,238),
"plum3"=>array(205,150,205),
"plum4"=>array(139,102,139),
"mediumorchid1"=>array(224,102,255),
"mediumorchid2"=>array(209,95,238),
"mediumorchid3"=>array(180,82,205),
"mediumorchid4"=>array(122,55,139),
"darkorchid1"=>array(191,62,255),
"darkorchid2"=>array(178,58,238),
"darkorchid3"=>array(154,50,205),
"darkorchid4"=>array(104,34,139),
"purple1"=>array(155,48,255),
"purple2"=>array(145,44,238),
"purple3"=>array(125,38,205),
"purple4"=>array(85,26,139),
"mediumpurple1"=>array(171,130,255),
"mediumpurple2"=>array(159,121,238),
"mediumpurple3"=>array(137,104,205),
"mediumpurple4"=>array(93,71,139),
"thistle1"=>array(255,225,255),
"thistle2"=>array(238,210,238),
"thistle3"=>array(205,181,205),
"thistle4"=>array(139,123,139),
"gray1"=>array(10,10,10),
"gray2"=>array(40,40,30),
"gray3"=>array(70,70,70),
"gray4"=>array(100,100,100),
"gray5"=>array(130,130,130),
"gray6"=>array(160,160,160),
"gray7"=>array(190,190,190),
"gray8"=>array(210,210,210),
"gray9"=>array(240,240,240),
"darkgray"=>array(100,100,100),
"darkblue"=>array(0,0,139),
"darkcyan"=>array(0,139,139),
"darkmagenta"=>array(139,0,139),
"darkred"=>array(139,0,0),
"silver"=>array(192, 192, 192),
"eggplant"=>array(144,176,168),
"lightgreen"=>array(144,238,144));
}
//----------------
// PUBLIC METHODS
// Colors can be specified as either
// 1. #xxxxxx HTML style
// 2. "colorname" as a named color
// 3. array(r,g,b) RGB triple
// This function translates this to a native RGB format and returns an
// RGB triple.
function Color($aColor) {
if (is_string($aColor)) {
 
// Strip of any alpha factor
$pos = strpos($aColor,'@');
if( $pos === false ) {
$alpha = 0;
}
else {
$pos2 = strpos($aColor,':');
if( $pos2===false )
$pos2 = $pos-1; // Sentinel
if( $pos > $pos2 ) {
$alpha = substr($aColor,$pos+1);
$aColor = substr($aColor,0,$pos);
}
else {
$alpha = substr($aColor,$pos+1,$pos2-$pos-1);
$aColor = substr($aColor,0,$pos).substr($aColor,$pos2);
}
}
 
// Extract potential adjustment figure at end of color
// specification
$pos = strpos($aColor,":");
if( $pos === false ) {
$adj = 1.0;
}
else {
$adj = 0.0 + substr($aColor,$pos+1);
$aColor = substr($aColor,0,$pos);
}
if( $adj < 0 )
JpGraphError::Raise('Adjustment factor for color must be > 0');
 
if (substr($aColor, 0, 1) == "#") {
$r = hexdec(substr($aColor, 1, 2));
$g = hexdec(substr($aColor, 3, 2));
$b = hexdec(substr($aColor, 5, 2));
} else {
if(!isset($this->rgb_table[$aColor]) )
JpGraphError::Raise(" Unknown color: <strong>$aColor</strong>");
$tmp=$this->rgb_table[$aColor];
$r = $tmp[0];
$g = $tmp[1];
$b = $tmp[2];
}
// Scale adj so that an adj=2 always
// makes the color 100% white (i.e. 255,255,255.
// and adj=1 neutral and adj=0 black.
if( $adj > 1 ) {
$m = ($adj-1.0)*(255-min(255,min($r,min($g,$b))));
return array(min(255,$r+$m), min(255,$g+$m), min(255,$b+$m),$alpha);
}
elseif( $adj < 1 ) {
$m = ($adj-1.0)*max(255,max($r,max($g,$b)));
return array(max(0,$r+$m), max(0,$g+$m), max(0,$b+$m),$alpha);
}
else {
return array($r,$g,$b,$alpha);
}
 
} elseif( is_array($aColor) ) {
if( count($aColor)==3 ) {
$aColor[3]=0;
return $aColor;
}
else
return $aColor;
}
else
JpGraphError::Raise(" Unknown color specification: $aColor , size=".count($aColor));
}
// Compare two colors
// return true if equal
function Equal($aCol1,$aCol2) {
$c1 = $this->Color($aCol1);
$c2 = $this->Color($aCol2);
if( $c1[0]==$c2[0] && $c1[1]==$c2[1] && $c1[2]==$c2[2] )
return true;
else
return false;
}
// Allocate a new color in the current image
// Return new color index, -1 if no more colors could be allocated
function Allocate($aColor,$aAlpha=0.0) {
list ($r, $g, $b, $a) = $this->color($aColor);
// If alpha is specified in the color string then this
// takes precedence over the second argument
if( $a > 0 )
$aAlpha = $a;
if(@$GLOBALS['gd2']==true) {
if( $aAlpha < 0 || $aAlpha > 1 ) {
JpGraphError::Raise('Alpha parameter for color must be between 0.0 and 1.0');
exit(1);
}
return imagecolorresolvealpha($this->img, $r, $g, $b, round($aAlpha * 127));
} else {
$index = imagecolorexact($this->img, $r, $g, $b);
if ($index == -1) {
$index = imagecolorallocate($this->img, $r, $g, $b);
if( USE_APPROX_COLORS && $index == -1 )
$index = imagecolorresolve($this->img, $r, $g, $b);
}
return $index;
}
}
} // Class
 
//===================================================
// CLASS Image
// Description: Wrapper class with some goodies to form the
// Interface to low level image drawing routines.
//===================================================
class Image {
var $img_format;
var $expired=true;
var $img;
var $left_margin=30,$right_margin=30,$top_margin=20,$bottom_margin=30;
var $plotwidth=0,$plotheight=0;
var $rgb=null;
var $current_color,$current_color_name;
var $lastx=0, $lasty=0;
var $width, $height;
var $line_weight=1;
var $line_style=1; // Default line style is solid
var $obs_list=array();
var $font_size=12,$font_family=FF_FONT1, $font_style=FS_NORMAL;
var $font_file='';
var $text_halign="left",$text_valign="bottom";
var $ttf=null;
var $use_anti_aliasing=false;
var $quality=null;
var $colorstack=array(),$colorstackidx=0;
var $canvascolor = 'white' ;
var $langconv = null ;
 
//---------------
// CONSTRUCTOR
function Image($aWidth,$aHeight,$aFormat=DEFAULT_GFORMAT) {
$this->CreateImgCanvas($aWidth,$aHeight);
$this->SetAutoMargin();
 
if( !$this->SetImgFormat($aFormat) ) {
JpGraphError::Raise("JpGraph: Selected graphic format is either not supported or unknown [$aFormat]");
}
$this->ttf = new TTF();
$this->langconv = new LanguageConv();
}
 
// Should we use anti-aliasing. Note: This really slows down graphics!
function SetAntiAliasing() {
$this->use_anti_aliasing=true;
}
 
function CreateRawCanvas($aWidth=0,$aHeight=0) {
if( @$GLOBALS['gd2']==true && USE_TRUECOLOR ) {
$this->img = @imagecreatetruecolor($aWidth, $aHeight);
if( $this->img < 1 ) {
die("<font color=red><b>JpGraph Error:</b></font> Can't create truecolor image. Check that you really have GD2 library installed.");
}
$this->SetAlphaBlending();
} else {
$this->img = @imagecreate($aWidth, $aHeight);
if( $this->img < 1 ) {
die("<font color=red><b>JpGraph Error:</b></font> Can't create image. Check that you really have the GD library installed.");
}
}
if( $this->rgb != null )
$this->rgb->img = $this->img ;
else
$this->rgb = new RGB($this->img);
}
 
function CloneCanvasH() {
$oldimage = $this->img;
$this->CreateRawCanvas($this->width,$this->height);
imagecopy($this->img,$oldimage,0,0,0,0,$this->width,$this->height);
return $oldimage;
}
function CreateImgCanvas($aWidth=0,$aHeight=0) {
 
$old = array($this->img,$this->width,$this->height);
$aWidth = round($aWidth);
$aHeight = round($aHeight);
 
$this->width=$aWidth;
$this->height=$aHeight;
 
if( $aWidth==0 || $aHeight==0 ) {
// We will set the final size later.
// Note: The size must be specified before any other
// img routines that stroke anything are called.
$this->img = null;
$this->rgb = null;
return $old;
}
$this->CreateRawCanvas($aWidth,$aHeight);
// Set canvas color (will also be the background color for a
// a pallett image
$this->SetColor($this->canvascolor);
$this->FilledRectangle(0,0,$aWidth,$aHeight);
 
return $old ;
}
 
function CopyCanvasH($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY,$aWidth,$aHeight,$aw=-1,$ah=-1) {
if( $aw === -1 ) {
$aw = $aWidth;
$ah = $aHeight;
$f = 'imagecopyresized';
}
else {
$f = $GLOBALS['copyfunc'] ;
}
$f($aToHdl,$aFromHdl,
$aToX,$aToY,$aFromX,$aFromY, $aWidth,$aHeight,$aw,$ah);
}
 
function SetCanvasH($aHdl) {
$this->img = $aHdl;
$this->rgb->img = $aHdl;
}
 
function SetCanvasColor($aColor) {
$this->canvascolor = $aColor ;
}
 
function SetAlphaBlending($aFlg=true) {
if( $GLOBALS['gd2'] )
ImageAlphaBlending($this->img,$aFlg);
else
JpGraphError::Raise('You only seem to have GD 1.x installed. To enable Alphablending requires GD 2.x or higher. Please install GD or make sure the constant USE_GD2 is specified correctly to reflect your installation. By default it tries to autodetect what version of GD you have installed. On some very rare occasions it may falsely detect GD2 where only GD1 is installed. You must then set USE_GD2 to false.');
}
 
function SetAutoMargin() {
GLOBAL $gJpgBrandTiming;
$min_bm=0;
if( $gJpgBrandTiming )
$min_bm=15;
$lm = max(0,$this->width/7);
$rm = max(0,$this->width/10);
$tm = max(0,$this->height/7);
$bm = max($min_bm,$this->height/7);
$this->SetMargin($lm,$rm,$tm,$bm);
}
 
//---------------
// PUBLIC METHODS
 
// Add observer. The observer will be notified when
// the margin changes
function AddObserver($aMethod,&$aObject) {
$this->obs_list[]=array($aMethod,&$aObject);
}
// Call all observers
function NotifyObservers() {
// foreach($this->obs_list as $o)
// $o[1]->$o[0]($this);
for($i=0; $i < count($this->obs_list); ++$i) {
$obj = & $this->obs_list[$i][1];
$method = $this->obs_list[$i][0];
$obj->$method($this);
}
}
function SetFont($family,$style=FS_NORMAL,$size=10) {
if($family==FONT1_BOLD || $family==FONT2_BOLD || $family==FONT0 || $family==FONT1 || $family==FONT2 )
JpGraphError::Raise(" Usage of FONT0, FONT1, FONT2 is deprecated. Use FF_xxx instead.");
$this->font_family=$family;
$this->font_style=$style;
$this->font_size=$size;
$this->font_file='';
if( ($this->font_family==FF_FONT1 || $this->font_family==FF_FONT2) && $this->font_style==FS_BOLD ){
++$this->font_family;
}
if( $this->font_family > FF_FONT2+1 ) { // A TTF font so get the font file
 
// Check that this PHP has support for TTF fonts
if( !function_exists('imagettfbbox') ) {
JpGraphError::Raise('This PHP build has not been configured with TTF support. You need to recompile your PHP installation with FreeType support.');
exit();
}
$this->font_file = $this->ttf->File($this->font_family,$this->font_style);
}
}
 
// Get the specific height for a text string
function GetTextHeight($txt="",$angle=0) {
$tmp = split("\n",$txt);
$n = count($tmp);
$m=0;
for($i=0; $i< $n; ++$i)
$m = max($m,strlen($tmp[$i]));
 
if( $this->font_family <= FF_FONT2+1 ) {
if( $angle==0 )
return $n*imagefontheight($this->font_family);
else
return $m*imagefontwidth($this->font_family);
}
else {
$bbox = $this->GetTTFBBox($txt,$angle);
return $bbox[1]-$bbox[5];
}
}
// Estimate font height
function GetFontHeight($angle=0) {
$txt = "XOMg";
return $this->GetTextHeight($txt,$angle);
}
// Approximate font width with width of letter "O"
function GetFontWidth($angle=0) {
$txt = 'O';
return $this->GetTextWidth($txt,$angle);
}
// Get actual width of text in absolute pixels
function GetTextWidth($txt,$angle=0) {
 
$tmp = split("\n",$txt);
$n = count($tmp);
if( $this->font_family <= FF_FONT2+1 ) {
 
$m=0;
for($i=0; $i < $n; ++$i) {
$l=strlen($tmp[$i]);
if( $l > $m ) {
$m = $l;
}
}
 
if( $angle==0 ) {
$width=$m*imagefontwidth($this->font_family);
return $width;
}
else {
// 90 degrees internal so height becomes width
return $n*imagefontheight($this->font_family);
}
}
else {
// For TTF fonts we must walk through a lines and find the
// widest one which we use as the width of the multi-line
// paragraph
$m=0;
for( $i=0; $i < $n; ++$i ) {
$bbox = $this->GetTTFBBox($tmp[$i],$angle);
$mm = $bbox[2] - $bbox[0];
if( $mm > $m )
$m = $mm;
}
return $m;
}
}
// Draw text with a box around it
function StrokeBoxedText($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black",
$shadowcolor=false,$paragraph_align="left",
$xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) {
 
if( !is_numeric($dir) ) {
if( $dir=="h" ) $dir=0;
elseif( $dir=="v" ) $dir=90;
else JpGraphError::Raise(" Unknown direction specified in call to StrokeBoxedText() [$dir]");
}
if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
$width=$this->GetTextWidth($txt,$dir) ;
$height=$this->GetTextHeight($txt,$dir) ;
}
else {
$width=$this->GetBBoxWidth($txt,$dir) ;
$height=$this->GetBBoxHeight($txt,$dir) ;
}
$height += 2*$ymarg;
$width += 2*$xmarg;
 
if( $this->text_halign=="right" ) $x -= $width;
elseif( $this->text_halign=="center" ) $x -= $width/2;
if( $this->text_valign=="bottom" ) $y -= $height;
elseif( $this->text_valign=="center" ) $y -= $height/2;
if( $shadowcolor ) {
$this->PushColor($shadowcolor);
$this->FilledRoundedRectangle($x-$xmarg+$dropwidth,$y-$ymarg+$dropwidth,
$x+$width+$dropwidth,$y+$height+$dropwidth,
$cornerradius);
$this->PopColor();
$this->PushColor($fcolor);
$this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
$this->PopColor();
$this->PushColor($bcolor);
$this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
$this->PopColor();
}
else {
if( $fcolor ) {
$oc=$this->current_color;
$this->SetColor($fcolor);
$this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
$this->current_color=$oc;
}
if( $bcolor ) {
$oc=$this->current_color;
$this->SetColor($bcolor);
$this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
$this->current_color=$oc;
}
}
$h=$this->text_halign;
$v=$this->text_valign;
$this->SetTextAlign("left","top");
$this->StrokeText($x, $y, $txt, $dir, $paragraph_align);
$this->SetTextAlign($h,$v);
}
 
// Set text alignment
function SetTextAlign($halign,$valign="bottom") {
$this->text_halign=$halign;
$this->text_valign=$valign;
}
 
function _StrokeBuiltinFont($x,$y,$txt,$dir=0,$paragraph_align="left") {
 
if( is_numeric($dir) && $dir!=90 && $dir!=0)
JpGraphError::Raise(" Internal font does not support drawing text at arbitrary angle. Use TTF fonts instead.");
$h=$this->GetTextHeight($txt);
$fh=$this->GetFontHeight();
$w=$this->GetTextWidth($txt);
if( $this->text_halign=="right")
$x -= $dir==0 ? $w : $h;
elseif( $this->text_halign=="center" ) {
// For center we subtract 1 pixel since this makes the middle
// be prefectly in the middle
$x -= $dir==0 ? $w/2-1 : $h/2;
}
if( $this->text_valign=="top" )
$y += $dir==0 ? $h : $w;
elseif( $this->text_valign=="center" )
$y += $dir==0 ? $h/2 : $w/2;
if( $dir==90 )
imagestringup($this->img,$this->font_family,$x,$y,$txt,$this->current_color);
else {
if( ereg("\n",$txt) ) {
$tmp = split("\n",$txt);
for($i=0; $i < count($tmp); ++$i) {
$w1 = $this->GetTextWidth($tmp[$i]);
if( $paragraph_align=="left" ) {
imagestring($this->img,$this->font_family,$x,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
}
elseif( $paragraph_align=="right" ) {
imagestring($this->img,$this->font_family,$x+($w-$w1),
$y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
}
else {
imagestring($this->img,$this->font_family,$x+$w/2-$w1/2,
$y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
}
}
}
else {
//Put the text
imagestring($this->img,$this->font_family,$x,$y-$h+1,$txt,$this->current_color);
}
}
}
 
function AddTxtCR($aTxt) {
// If the user has just specified a '\n'
// instead of '\n\t' we have to add '\r' since
// the width will be too muchy otherwise since when
// we print we stroke the individually lines by hand.
$e = explode("\n",$aTxt);
$n = count($e);
for($i=0; $i<$n; ++$i) {
$e[$i]=str_replace("\r","",$e[$i]);
}
return implode("\n\r",$e);
}
 
function GetTTFBBox($aTxt,$aAngle=0) {
$bbox = @ImageTTFBBox($this->font_size,$aAngle,$this->font_file,$aTxt);
if( $bbox === false ) {
JpGraphError::Raise("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library.");
}
return $bbox;
}
 
function GetBBoxTTF($aTxt,$aAngle=0) {
// Normalize the bounding box to become a minimum
// enscribing rectangle
 
$aTxt = $this->AddTxtCR($aTxt);
 
if( !is_readable($this->font_file) ) {
JpGraphError::Raise('Can not read font file ('.$this->font_file.') in call to Image::GetBBoxTTF. Please make sure that you have set a font before calling this method and that the font is installed in the TTF directory.');
}
$bbox = $this->GetTTFBBox($aTxt,$aAngle);
 
if( $aAngle==0 )
return $bbox;
if( $aAngle >= 0 ) {
if( $aAngle <= 90 ) { //<=0
$bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
$bbox[2],$bbox[5],$bbox[6],$bbox[5]);
}
elseif( $aAngle <= 180 ) { //<= 2
$bbox = array($bbox[4],$bbox[7],$bbox[0],$bbox[7],
$bbox[0],$bbox[3],$bbox[4],$bbox[3]);
}
elseif( $aAngle <= 270 ) { //<= 3
$bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
$bbox[6],$bbox[1],$bbox[2],$bbox[1]);
}
else {
$bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
$bbox[4],$bbox[7],$bbox[0],$bbox[7]);
}
}
elseif( $aAngle < 0 ) {
if( $aAngle <= -270 ) { // <= -3
$bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
$bbox[2],$bbox[5],$bbox[6],$bbox[5]);
}
elseif( $aAngle <= -180 ) { // <= -2
$bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
$bbox[4],$bbox[7],$bbox[0],$bbox[7]);
}
elseif( $aAngle <= -90 ) { // <= -1
$bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
$bbox[6],$bbox[1],$bbox[2],$bbox[1]);
}
else {
$bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
$bbox[4],$bbox[7],$bbox[0],$bbox[7]);
}
}
return $bbox;
}
 
function GetBBoxHeight($aTxt,$aAngle=0) {
$box = $this->GetBBoxTTF($aTxt,$aAngle);
return $box[1]-$box[7]+1;
}
 
function GetBBoxWidth($aTxt,$aAngle=0) {
$box = $this->GetBBoxTTF($aTxt,$aAngle);
return $box[2]-$box[0]+1;
}
 
function _StrokeTTF($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
 
// Remember the anchor point before adjustment
if( $debug ) {
$ox=$x;
$oy=$y;
}
 
if( !ereg("\n",$txt) || ($dir>0 && ereg("\n",$txt)) ) {
// Format a single line
 
$txt = $this->AddTxtCR($txt);
 
$bbox=$this->GetBBoxTTF($txt,$dir);
// Align x,y ot lower left corner of bbox
$x -= $bbox[0];
$y -= $bbox[1];
 
// Note to self: "topanchor" is deprecated after we changed the
// bopunding box stuff.
if( $this->text_halign=="right" || $this->text_halign=="topanchor" )
$x -= $bbox[2]-$bbox[0];
elseif( $this->text_halign=="center" ) $x -= ($bbox[2]-$bbox[0])/2;
if( $this->text_valign=="top" ) $y += abs($bbox[5])+$bbox[1];
elseif( $this->text_valign=="center" ) $y -= ($bbox[5]-$bbox[1])/2;
 
ImageTTFText ($this->img, $this->font_size, $dir, $x, $y,
$this->current_color,$this->font_file,$txt);
 
if( $debug ) {
// Draw the bounding rectangle and the bounding box
$box=ImageTTFBBox($this->font_size,$dir,$this->font_file,$txt);
$p = array();
$p1 = array();
for($i=0; $i < 4; ++$i) {
$p[] = $bbox[$i*2]+$x;
$p[] = $bbox[$i*2+1]+$y;
$p1[] = $box[$i*2]+$x;
$p1[] = $box[$i*2+1]+$y;
}
 
// Draw bounding box
$this->PushColor('green');
$this->Polygon($p1,true);
$this->PopColor();
// Draw bounding rectangle
$this->PushColor('darkgreen');
$this->Polygon($p,true);
$this->PopColor();
// Draw a cross at the anchor point
$this->PushColor('red');
$this->Line($ox-15,$oy,$ox+15,$oy);
$this->Line($ox,$oy-15,$ox,$oy+15);
$this->PopColor();
}
}
else {
// Format a text paragraph
$fh=$this->GetFontHeight();
 
// Line margin is 15% of font height
$linemargin=round($fh*0.15);
$fh += $linemargin;
$w=$this->GetTextWidth($txt);
 
$y -= $linemargin/2;
$tmp = split("\n",$txt);
$nl = count($tmp);
$h = $nl * $fh;
 
if( $this->text_halign=="right")
$x -= $dir==0 ? $w : $h;
elseif( $this->text_halign=="center" ) {
$x -= $dir==0 ? $w/2 : $h/2;
}
if( $this->text_valign=="top" )
$y += $dir==0 ? $h : $w;
elseif( $this->text_valign=="center" )
$y += $dir==0 ? $h/2 : $w/2;
 
// Here comes a tricky bit.
// Since we have to give the position for the string at the
// baseline this means thaht text will move slightly up
// and down depending on any of it's character descend below
// the baseline, for example a 'g'. To adjust the Y-position
// we therefore adjust the text with the baseline Y-offset
// as used for the current font and size. This will keep the
// baseline at a fixed positoned disregarding the actual
// characters in the string.
$standardbox = $this->GetTTFBBox('Gg',$dir);
$yadj = $standardbox[1];
$xadj = $standardbox[0];
for($i=0; $i < $nl; ++$i) {
$wl = $this->GetTextWidth($tmp[$i]);
$bbox = $this->GetTTFBBox($tmp[$i],$dir);
if( $paragraph_align=="left" ) {
$xl = $x;
}
elseif( $paragraph_align=="right" ) {
$xl = $x + ($w-$wl);
}
else {
// Center
$xl = $x + $w/2 - $wl/2 ;
}
 
$xl -= $bbox[0];
$yl = $y - $yadj;
$xl = $xl - $xadj;
ImageTTFText ($this->img, $this->font_size, $dir,
$xl, $yl-($h-$fh)+$fh*$i,
$this->current_color,$this->font_file,$tmp[$i]);
 
 
if( $debug ) {
// Draw the bounding rectangle around each line
$box=ImageTTFBBox($this->font_size,$dir,$this->font_file,$tmp[$i]);
$p = array();
for($j=0; $j < 4; ++$j) {
$p[] = $bbox[$j*2]+$xl;
$p[] = $bbox[$j*2+1]+$yl-($h-$fh)+$fh*$i;
}
// Draw bounding rectangle
$this->PushColor('darkgreen');
$this->Polygon($p,true);
$this->PopColor();
}
}
 
if( $debug ) {
// Draw a cross at the anchor point
$this->PushColor('red');
$this->Line($ox-25,$oy,$ox+25,$oy);
$this->Line($ox,$oy-25,$ox,$oy+25);
$this->PopColor();
}
 
}
}
function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
 
$x = round($x);
$y = round($y);
 
// Do special language encoding
$txt = $this->langconv->Convert($txt,$this->font_family);
 
if( !is_numeric($dir) )
JpGraphError::Raise(" Direction for text most be given as an angle between 0 and 90.");
if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
$this->_StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,$debug);
}
elseif($this->font_family >= FF_COURIER && $this->font_family <= FF_BOOK) {
$this->_StrokeTTF($x,$y,$txt,$dir,$paragraph_align,$debug);
}
else
JpGraphError::Raise(" Unknown font font family specification. ");
}
function SetMargin($lm,$rm,$tm,$bm) {
$this->left_margin=$lm;
$this->right_margin=$rm;
$this->top_margin=$tm;
$this->bottom_margin=$bm;
$this->plotwidth=$this->width - $this->left_margin-$this->right_margin ;
$this->plotheight=$this->height - $this->top_margin-$this->bottom_margin ;
if( $this->plotwidth < 0 || $this->plotheight < 0 )
JpGraphError::raise("To small plot area. ($lm,$rm,$tm,$bm : $this->plotwidth x $this->plotheight). With the given image size and margins there is to little space left for the plot. Increase the plot size or reduce the margins.");
$this->NotifyObservers();
}
 
function SetTransparent($color) {
imagecolortransparent ($this->img,$this->rgb->allocate($color));
}
function SetColor($color,$aAlpha=0) {
$this->current_color_name = $color;
$this->current_color=$this->rgb->allocate($color,$aAlpha);
if( $this->current_color == -1 ) {
$tc=imagecolorstotal($this->img);
JpGraphError::Raise("Can't allocate any more colors.
Image has already allocated maximum of <b>$tc colors</b>.
This might happen if you have anti-aliasing turned on
together with a background image or perhaps gradient fill
since this requires many, many colors. Try to turn off
anti-aliasing.<p>
If there is still a problem try downgrading the quality of
the background image to use a smaller pallete to leave some
entries for your graphs. You should try to limit the number
of colors in your background image to 64.<p>
If there is still problem set the constant
<pre>
DEFINE(\"USE_APPROX_COLORS\",true);
</pre>
in jpgraph.php This will use approximative colors
when the palette is full.
<p>
Unfortunately there is not much JpGraph can do about this
since the palette size is a limitation of current graphic format and
what the underlying GD library suppports.");
}
return $this->current_color;
}
function PushColor($color) {
if( $color != "" ) {
$this->colorstack[$this->colorstackidx]=$this->current_color_name;
$this->colorstack[$this->colorstackidx+1]=$this->current_color;
$this->colorstackidx+=2;
$this->SetColor($color);
}
else {
JpGraphError::Raise("Color specified as empty string in PushColor().");
}
}
function PopColor() {
if($this->colorstackidx<1)
JpGraphError::Raise(" Negative Color stack index. Unmatched call to PopColor()");
$this->current_color=$this->colorstack[--$this->colorstackidx];
$this->current_color_name=$this->colorstack[--$this->colorstackidx];
}
// Why this duplication? Because this way we can call this method
// for any image and not only the current objsct
function AdjSat($sat) { $this->_AdjSat($this->img,$sat); }
function _AdjSat($img,$sat) {
$nbr = imagecolorstotal ($img);
for( $i=0; $i<$nbr; ++$i ) {
$colarr = imagecolorsforindex ($img,$i);
$rgb[0]=$colarr["red"];
$rgb[1]=$colarr["green"];
$rgb[2]=$colarr["blue"];
$rgb = $this->AdjRGBSat($rgb,$sat);
imagecolorset ($img, $i, $rgb[0], $rgb[1], $rgb[2]);
}
}
function AdjBrightContrast($bright,$contr=0) {
$this->_AdjBrightContrast($this->img,$bright,$contr);
}
function _AdjBrightContrast($img,$bright,$contr=0) {
if( $bright < -1 || $bright > 1 || $contr < -1 || $contr > 1 )
JpGraphError::Raise(" Parameters for brightness and Contrast out of range [-1,1]");
$nbr = imagecolorstotal ($img);
for( $i=0; $i<$nbr; ++$i ) {
$colarr = imagecolorsforindex ($img,$i);
$r = $this->AdjRGBBrightContrast($colarr["red"],$bright,$contr);
$g = $this->AdjRGBBrightContrast($colarr["green"],$bright,$contr);
$b = $this->AdjRGBBrightContrast($colarr["blue"],$bright,$contr);
imagecolorset ($img, $i, $r, $g, $b);
}
}
// Private helper function for adj sat
// Adjust saturation for RGB array $u. $sat is a value between -1 and 1
// Note: Due to GD inability to handle true color the RGB values are only between
// 8 bit. This makes saturation quite sensitive for small increases in parameter sat.
//
// Tip: To get a grayscale picture set sat=-100, values <-100 changes the colors
// to it's complement.
//
// Implementation note: The saturation is implemented directly in the RGB space
// by adjusting the perpendicular distance between the RGB point and the "grey"
// line (1,1,1). Setting $sat>0 moves the point away from the line along the perp.
// distance and a negative value moves the point closer to the line.
// The values are truncated when the color point hits the bounding box along the
// RGB axis.
// DISCLAIMER: I'm not 100% sure this is he correct way to implement a color
// saturation function in RGB space. However, it looks ok and has the expected effect.
function AdjRGBSat($rgb,$sat) {
// TODO: Should be moved to the RGB class
// Grey vector
$v=array(1,1,1);
 
// Dot product
$dot = $rgb[0]*$v[0]+$rgb[1]*$v[1]+$rgb[2]*$v[2];
 
// Normalize dot product
$normdot = $dot/3; // dot/|v|^2
 
// Direction vector between $u and its projection onto $v
for($i=0; $i<3; ++$i)
$r[$i] = $rgb[$i] - $normdot*$v[$i];
 
// Adjustment factor so that sat==1 sets the highest RGB value to 255
if( $sat > 0 ) {
$m=0;
for( $i=0; $i<3; ++$i) {
if( sign($r[$i]) == 1 && $r[$i]>0)
$m=max($m,(255-$rgb[$i])/$r[$i]);
}
$tadj=$m;
}
else
$tadj=1;
$tadj = $tadj*$sat;
for($i=0; $i<3; ++$i) {
$un[$i] = round($rgb[$i] + $tadj*$r[$i]);
if( $un[$i]<0 ) $un[$i]=0; // Truncate color when they reach 0
if( $un[$i]>255 ) $un[$i]=255;// Avoid potential rounding error
}
return $un;
}
 
// Private helper function for AdjBrightContrast
function AdjRGBBrightContrast($rgb,$bright,$contr) {
// TODO: Should be moved to the RGB class
// First handle contrast, i.e change the dynamic range around grey
if( $contr <= 0 ) {
// Decrease contrast
$adj = abs($rgb-128) * (-$contr);
if( $rgb < 128 ) $rgb += $adj;
else $rgb -= $adj;
}
else { // $contr > 0
// Increase contrast
if( $rgb < 128 ) $rgb = $rgb - ($rgb * $contr);
else $rgb = $rgb + ((255-$rgb) * $contr);
}
// Add (or remove) various amount of white
$rgb += $bright*255;
$rgb=min($rgb,255);
$rgb=max($rgb,0);
return $rgb;
}
function SetLineWeight($weight) {
$this->line_weight = $weight;
}
function SetStartPoint($x,$y) {
$this->lastx=round($x);
$this->lasty=round($y);
}
function Arc($cx,$cy,$w,$h,$s,$e) {
// GD Arc doesn't like negative angles
while( $s < 0) $s += 360;
while( $e < 0) $e += 360;
imagearc($this->img,round($cx),round($cy),round($w),round($h),
$s,$e,$this->current_color);
}
function FilledArc($xc,$yc,$w,$h,$s,$e,$style="") {
 
if( $GLOBALS['gd2'] ) {
while( $s < 0 ) $s += 360;
while( $e < 0 ) $e += 360;
if( $style=="" )
$style=IMG_ARC_PIE;
imagefilledarc($this->img,round($xc),round($yc),round($w),round($h),
round($s),round($e),$this->current_color,$style);
return;
}
 
 
// In GD 1.x we have to do it ourself interesting enough there is surprisingly
// little difference in time between doing it PHP and using the optimised GD
// library (roughly ~20%) I had expected it to be at least 100% slower doing it
// manually with a polygon approximation in PHP.....
$fillcolor = $this->current_color_name;
 
$w /= 2; // We use radius in our calculations instead
$h /= 2;
 
// Setup the angles so we have the same conventions as the builtin
// FilledArc() which is a little bit strange if you ask me....
 
$s = 360-$s;
$e = 360-$e;
 
if( $e > $s ) {
$e = $e - 360;
$da = $s - $e;
}
$da = $s-$e;
 
// We use radians
$s *= M_PI/180;
$e *= M_PI/180;
$da *= M_PI/180;
 
// Calculate a polygon approximation
$p[0] = $xc;
$p[1] = $yc;
 
// Heuristic on how many polygons we need to make the
// arc look good
$numsteps = round(8 * abs($da) * ($w+$h)*($w+$h)/1500);
 
if( $numsteps == 0 ) return;
if( $numsteps < 7 ) $numsteps=7;
$delta = abs($da)/$numsteps;
$pa=array();
$a = $s;
for($i=1; $i<=$numsteps; ++$i ) {
$p[2*$i] = round($xc + $w*cos($a));
$p[2*$i+1] = round($yc - $h*sin($a));
//$a = $s + $i*$delta;
$a -= $delta;
$pa[2*($i-1)] = $p[2*$i];
$pa[2*($i-1)+1] = $p[2*$i+1];
}
 
// Get the last point at the exact ending angle to avoid
// any rounding errors.
$p[2*$i] = round($xc + $w*cos($e));
$p[2*$i+1] = round($yc - $h*sin($e));
$pa[2*($i-1)] = $p[2*$i];
$pa[2*($i-1)+1] = $p[2*$i+1];
$i++;
 
$p[2*$i] = $xc;
$p[2*$i+1] = $yc;
if( $fillcolor != "" ) {
$this->PushColor($fillcolor);
imagefilledpolygon($this->img,$p,count($p)/2,$this->current_color);
$this->PopColor();
}
}
 
function FilledCakeSlice($cx,$cy,$w,$h,$s,$e) {
$this->CakeSlice($cx,$cy,$w,$h,$s,$e,$this->current_color_name);
}
 
function CakeSlice($xc,$yc,$w,$h,$s,$e,$fillcolor="",$arccolor="") {
$s = round($s); $e = round($e);
$w = round($w); $h = round($h);
$xc = round($xc); $yc = round($yc);
$this->PushColor($fillcolor);
$this->FilledArc($xc,$yc,2*$w,2*$h,$s,$e);
$this->PopColor();
if( $arccolor != "" ) {
$this->PushColor($arccolor);
// We add 2 pixels to make the Arc() better aligned with
// the filled arc.
if( $GLOBALS['gd2'] ) {
imagefilledarc($this->img,$xc,$yc,2*$w,2*$h,$s,$e,$this->current_color_name,
IMG_ARC_NOFILL | IMG_ARC_EDGED ) ;
}
else {
$this->Arc($xc,$yc,2*$w+2,2*$h+2,$s,$e);
$xx = $w * cos(2*M_PI - $s*M_PI/180) + $xc;
$yy = $yc - $h * sin(2*M_PI - $s*M_PI/180);
$this->Line($xc,$yc,$xx,$yy);
$xx = $w * cos(2*M_PI - $e*M_PI/180) + $xc;
$yy = $yc - $h * sin(2*M_PI - $e*M_PI/180);
$this->Line($xc,$yc,$xx,$yy);
}
$this->PopColor();
}
}
 
function Ellipse($xc,$yc,$w,$h) {
$this->Arc($xc,$yc,$w,$h,0,360);
}
// Breseham circle gives visually better result then using GD
// built in arc(). It takes some more time but gives better
// accuracy.
function BresenhamCircle($xc,$yc,$r) {
$d = 3-2*$r;
$x = 0;
$y = $r;
while($x<=$y) {
$this->Point($xc+$x,$yc+$y);
$this->Point($xc+$x,$yc-$y);
$this->Point($xc-$x,$yc+$y);
$this->Point($xc-$x,$yc-$y);
$this->Point($xc+$y,$yc+$x);
$this->Point($xc+$y,$yc-$x);
$this->Point($xc-$y,$yc+$x);
$this->Point($xc-$y,$yc-$x);
if( $d<0 ) $d += 4*$x+6;
else {
$d += 4*($x-$y)+10;
--$y;
}
++$x;
}
}
function Circle($xc,$yc,$r) {
if( USE_BRESENHAM )
$this->BresenhamCircle($xc,$yc,$r);
else {
 
/*
// Some experimental code snippet to see if we can get a decent
// result doing a trig-circle
// Create an approximated circle with 0.05 rad resolution
$end = 2*M_PI;
$l = $r/10;
if( $l < 3 ) $l=3;
$step_size = 2*M_PI/(2*$r*M_PI/$l);
$pts = array();
$pts[] = $r + $xc;
$pts[] = $yc;
for( $a=$step_size; $a <= $end; $a += $step_size ) {
$pts[] = round($xc + $r*cos($a));
$pts[] = round($yc - $r*sin($a));
}
imagepolygon($this->img,$pts,count($pts)/2,$this->current_color);
*/
 
$this->Arc($xc,$yc,$r*2,$r*2,0,360);
 
// For some reason imageellipse() isn't in GD 2.0.1, PHP 4.1.1
//imageellipse($this->img,$xc,$yc,$r,$r,$this->current_color);
}
}
function FilledCircle($xc,$yc,$r) {
if( $GLOBALS['gd2'] ) {
imagefilledellipse($this->img,round($xc),round($yc),
2*$r,2*$r,$this->current_color);
}
else {
for( $i=1; $i < 2*$r; $i += 2 ) {
// To avoid moire patterns we have to draw some
// 1 extra "skewed" filled circles
$this->Arc($xc,$yc,$i,$i,0,360);
$this->Arc($xc,$yc,$i+1,$i,0,360);
$this->Arc($xc,$yc,$i+1,$i+1,0,360);
}
}
}
// Linear Color InterPolation
function lip($f,$t,$p) {
$p = round($p,1);
$r = $f[0] + ($t[0]-$f[0])*$p;
$g = $f[1] + ($t[1]-$f[1])*$p;
$b = $f[2] + ($t[2]-$f[2])*$p;
return array($r,$g,$b);
}
 
// Anti-aliased line.
// Note that this is roughly 8 times slower then a normal line!
function WuLine($x1,$y1,$x2,$y2) {
// Get foreground line color
$lc = imagecolorsforindex($this->img,$this->current_color);
$lc = array($lc["red"],$lc["green"],$lc["blue"]);
 
$dx = $x2-$x1;
$dy = $y2-$y1;
if( abs($dx) > abs($dy) ) {
if( $dx<0 ) {
$dx = -$dx;$dy = -$dy;
$tmp=$x2;$x2=$x1;$x1=$tmp;
$tmp=$y2;$y2=$y1;$y1=$tmp;
}
$x=$x1<<16; $y=$y1<<16;
$yinc = ($dy*65535)/$dx;
while( ($x >> 16) < $x2 ) {
$bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
if( $bc <= 0 ) {
JpGraphError::Raise('Problem with color palette and your GD setup. Please disable anti-aliasing or use GD2 with true-color. If you have GD2 library installed please make sure that you have set the USE_GD2 constant to true and that truecolor is enabled.');
}
$bc=array($bc["red"],$bc["green"],$bc["blue"]);
$this->SetColor($this->lip($lc,$bc,($y & 0xFFFF)/65535));
imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
$this->SetColor($this->lip($lc,$bc,(~$y & 0xFFFF)/65535));
imagesetpixel($this->img,$x>>16,($y>>16)+1,$this->current_color);
$x += 65536; $y += $yinc;
}
}
else {
if( $dy<0 ) {
$dx = -$dx;$dy = -$dy;
$tmp=$x2;$x2=$x1;$x1=$tmp;
$tmp=$y2;$y2=$y1;$y1=$tmp;
}
$x=$x1<<16; $y=$y1<<16;
$xinc = ($dx*65535)/$dy;
while( ($y >> 16) < $y2 ) {
$bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
if( $bc <= 0 ) {
JpGraphError::Raise('Problem with color palette and your GD setup. Please disable anti-aliasing or use GD2 with true-color. If you have GD2 library installed please make sure that you have set the USE_GD2 constant to true and truecolor is enabled.');
 
}
 
$bc=array($bc["red"],$bc["green"],$bc["blue"]);
$this->SetColor($this->lip($lc,$bc,($x & 0xFFFF)/65535));
imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
$this->SetColor($this->lip($lc,$bc,(~$x & 0xFFFF)/65535));
imagesetpixel($this->img,($x>>16)+1,$y>>16,$this->current_color);
$y += 65536; $x += $xinc;
}
}
$this->SetColor($lc);
imagesetpixel($this->img,$x2,$y2,$this->current_color);
imagesetpixel($this->img,$x1,$y1,$this->current_color);
}
 
// Set line style dashed, dotted etc
function SetLineStyle($s) {
if( is_numeric($s) ) {
if( $s<1 || $s>4 )
JpGraphError::Raise(" Illegal numeric argument to SetLineStyle(): ($s)");
}
elseif( is_string($s) ) {
if( $s == "solid" ) $s=1;
elseif( $s == "dotted" ) $s=2;
elseif( $s == "dashed" ) $s=3;
elseif( $s == "longdashed" ) $s=4;
else JpGraphError::Raise(" Illegal string argument to SetLineStyle(): $s");
}
else JpGraphError::Raise(" Illegal argument to SetLineStyle $s");
$this->line_style=$s;
}
// Same as Line but take the line_style into account
function StyleLine($x1,$y1,$x2,$y2) {
switch( $this->line_style ) {
case 1:// Solid
$this->Line($x1,$y1,$x2,$y2);
break;
case 2: // Dotted
$this->DashedLine($x1,$y1,$x2,$y2,1,6);
break;
case 3: // Dashed
$this->DashedLine($x1,$y1,$x2,$y2,2,4);
break;
case 4: // Longdashes
$this->DashedLine($x1,$y1,$x2,$y2,8,6);
break;
default:
JpGraphError::Raise(" Unknown line style: $this->line_style ");
break;
}
}
 
function Line($x1,$y1,$x2,$y2) {
 
$x1 = round($x1);
$x2 = round($x2);
$y1 = round($y1);
$y2 = round($y2);
 
if( $this->line_weight==0 ) return;
if( $this->use_anti_aliasing ) {
$dx = $x2-$x1;
$dy = $y2-$y1;
// Vertical, Horizontal or 45 lines don't need anti-aliasing
if( $dx!=0 && $dy!=0 && $dx!=$dy ) {
$this->WuLine($x1,$y1,$x2,$y2);
return;
}
}
if( $this->line_weight==1 ) {
imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
}
elseif( $x1==$x2 ) { // Special case for vertical lines
imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
$w1=floor($this->line_weight/2);
$w2=floor(($this->line_weight-1)/2);
for($i=1; $i<=$w1; ++$i)
imageline($this->img,$x1+$i,$y1,$x2+$i,$y2,$this->current_color);
for($i=1; $i<=$w2; ++$i)
imageline($this->img,$x1-$i,$y1,$x2-$i,$y2,$this->current_color);
}
elseif( $y1==$y2 ) { // Special case for horizontal lines
imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
$w1=floor($this->line_weight/2);
$w2=floor(($this->line_weight-1)/2);
for($i=1; $i<=$w1; ++$i)
imageline($this->img,$x1,$y1+$i,$x2,$y2+$i,$this->current_color);
for($i=1; $i<=$w2; ++$i)
imageline($this->img,$x1,$y1-$i,$x2,$y2-$i,$this->current_color);
}
else { // General case with a line at an angle
$a = atan2($y1-$y2,$x2-$x1);
// Now establish some offsets from the center. This gets a little
// bit involved since we are dealing with integer functions and we
// want the apperance to be as smooth as possible and never be thicker
// then the specified width.
// We do the trig stuff to make sure that the endpoints of the line
// are perpendicular to the line itself.
$dx=(sin($a)*$this->line_weight/2);
$dy=(cos($a)*$this->line_weight/2);
 
$pnts = array($x2+$dx,$y2+$dy,$x2-$dx,$y2-$dy,$x1-$dx,$y1-$dy,$x1+$dx,$y1+$dy);
imagefilledpolygon($this->img,$pnts,count($pnts)/2,$this->current_color);
}
$this->lastx=$x2; $this->lasty=$y2;
}
 
function Polygon($p,$closed=FALSE) {
if( $this->line_weight==0 ) return;
$n=count($p);
$oldx = $p[0];
$oldy = $p[1];
for( $i=2; $i < $n; $i+=2 ) {
$this->Line($oldx,$oldy,$p[$i],$p[$i+1]);
$oldx = $p[$i];
$oldy = $p[$i+1];
}
if( $closed )
$this->Line($oldx,$oldy,$p[0],$p[1]);
}
function FilledPolygon($pts) {
$n=count($pts);
for($i=0; $i < $n; ++$i)
$pts[$i] = round($pts[$i]);
imagefilledpolygon($this->img,$pts,count($pts)/2,$this->current_color);
}
function Rectangle($xl,$yu,$xr,$yl) {
$this->Polygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl,$xl,$yu));
}
function FilledRectangle($xl,$yu,$xr,$yl) {
$this->FilledPolygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl));
}
 
function FilledRectangle2($xl,$yu,$xr,$yl,$color1,$color2,$style=1) {
// Fill a rectangle with lines of two colors
if( $style===1 ) {
// Horizontal stripe
if( $yl < $yu ) {
$t = $yl; $yl=$yu; $yu=$t;
}
for( $y=$yu; $y <= $yl; ++$y) {
$this->SetColor($color1);
$this->Line($xl,$y,$xr,$y);
++$y;
$this->SetColor($color2);
$this->Line($xl,$y,$xr,$y);
}
}
else {
if( $xl < $xl ) {
$t = $xl; $xl=$xr; $xr=$t;
}
for( $x=$xl; $x <= $xr; ++$x) {
$this->SetColor($color1);
$this->Line($x,$yu,$x,$yl);
++$x;
$this->SetColor($color2);
$this->Line($x,$yu,$x,$yl);
}
}
}
 
function ShadowRectangle($xl,$yu,$xr,$yl,$fcolor=false,$shadow_width=3,$shadow_color=array(102,102,102)) {
// This is complicated by the fact that we must also handle the case where
// the reactangle has no fill color
$this->PushColor($shadow_color);
$this->FilledRectangle($xr-$shadow_width,$yu+$shadow_width,$xr,$yl-$shadow_width-1);
$this->FilledRectangle($xl+$shadow_width,$yl-$shadow_width,$xr,$yl);
//$this->FilledRectangle($xl+$shadow_width,$yu+$shadow_width,$xr,$yl);
$this->PopColor();
if( $fcolor==false )
$this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
else {
$this->PushColor($fcolor);
$this->FilledRectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
$this->PopColor();
$this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
}
}
 
function FilledRoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
if( $r==0 ) {
$this->FilledRectangle($xt,$yt,$xr,$yl);
return;
}
 
// To avoid overlapping fillings (which will look strange
// when alphablending is enabled) we have no choice but
// to fill the five distinct areas one by one.
// Center square
$this->FilledRectangle($xt+$r,$yt+$r,$xr-$r,$yl-$r);
// Top band
$this->FilledRectangle($xt+$r,$yt,$xr-$r,$yt+$r-1);
// Bottom band
$this->FilledRectangle($xt+$r,$yl-$r+1,$xr-$r,$yl);
// Left band
$this->FilledRectangle($xt,$yt+$r+1,$xt+$r-1,$yl-$r);
// Right band
$this->FilledRectangle($xr-$r+1,$yt+$r,$xr,$yl-$r);
 
// Topleft & Topright arc
$this->FilledArc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
$this->FilledArc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
 
// Bottomleft & Bottom right arc
$this->FilledArc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
$this->FilledArc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
 
}
 
function RoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
 
if( $r==0 ) {
$this->Rectangle($xt,$yt,$xr,$yl);
return;
}
 
// Top & Bottom line
$this->Line($xt+$r,$yt,$xr-$r,$yt);
$this->Line($xt+$r,$yl,$xr-$r,$yl);
 
// Left & Right line
$this->Line($xt,$yt+$r,$xt,$yl-$r);
$this->Line($xr,$yt+$r,$xr,$yl-$r);
 
// Topleft & Topright arc
$this->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
$this->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
 
// Bottomleft & Bottomright arc
$this->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
$this->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
}
 
function FilledBevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='darkgray@0.4') {
$this->FilledRectangle($x1,$y1,$x2,$y2);
$this->Bevel($x1,$y1,$x2,$y2,$depth,$color1,$color2);
}
 
function Bevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='black@0.5') {
$this->PushColor($color1);
for( $i=0; $i < $depth; ++$i ) {
$this->Line($x1+$i,$y1+$i,$x1+$i,$y2-$i);
$this->Line($x1+$i,$y1+$i,$x2-$i,$y1+$i);
}
$this->PopColor();
$this->PushColor($color2);
for( $i=0; $i < $depth; ++$i ) {
$this->Line($x1+$i,$y2-$i,$x2-$i,$y2-$i);
$this->Line($x2-$i,$y1+$i,$x2-$i,$y2-$i-1);
}
$this->PopColor();
}
 
function StyleLineTo($x,$y) {
$this->StyleLine($this->lastx,$this->lasty,$x,$y);
$this->lastx=$x;
$this->lasty=$y;
}
function LineTo($x,$y) {
$this->Line($this->lastx,$this->lasty,$x,$y);
$this->lastx=$x;
$this->lasty=$y;
}
function Point($x,$y) {
imagesetpixel($this->img,round($x),round($y),$this->current_color);
}
function Fill($x,$y) {
imagefill($this->img,round($x),round($y),$this->current_color);
}
 
function FillToBorder($x,$y,$aBordColor) {
$bc = $this->rgb->allocate($aBordColor);
if( $bc == -1 ) {
JpGraphError::Raise('Image::FillToBorder : Can not allocate more colors');
exit();
}
imagefilltoborder($this->img,round($x),round($y),$bc,$this->current_color);
}
function DashedLine($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) {
// Code based on, but not identical to, work by Ariel Garza and James Pine
$line_length = ceil (sqrt(pow(($x2 - $x1),2) + pow(($y2 - $y1),2)) );
$dx = ($line_length) ? ($x2 - $x1) / $line_length : 0;
$dy = ($line_length) ? ($y2 - $y1) / $line_length : 0;
$lastx = $x1; $lasty = $y1;
$xmax = max($x1,$x2);
$xmin = min($x1,$x2);
$ymax = max($y1,$y2);
$ymin = min($y1,$y2);
for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
$x = ($dash_length * $dx) + $lastx;
$y = ($dash_length * $dy) + $lasty;
// The last section might overshoot so we must take a computational hit
// and check this.
if( $x>$xmax ) $x=$xmax;
if( $y>$ymax ) $y=$ymax;
if( $x<$xmin ) $x=$xmin;
if( $y<$ymin ) $y=$ymin;
 
$this->Line($lastx,$lasty,$x,$y);
$lastx = $x + ($dash_space * $dx);
$lasty = $y + ($dash_space * $dy);
}
}
 
function SetExpired($aFlg=true) {
$this->expired = $aFlg;
}
// Generate image header
function Headers() {
if ($this->expired) {
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
}
header("Content-type: image/$this->img_format");
}
 
// Adjust image quality for formats that allow this
function SetQuality($q) {
$this->quality = $q;
}
// Stream image to browser or to file
function Stream($aFile="") {
$func="image".$this->img_format;
if( $this->img_format=="jpeg" && $this->quality != null ) {
$res = @$func($this->img,$aFile,$this->quality);
}
else {
if( $aFile != "" ) {
$res = @$func($this->img,$aFile);
}
else
$res = @$func($this->img);
}
if( !$res )
JpGraphError::Raise("Can't create or stream image to file $aFile Check that PHP has enough permission to write a file to the current directory.");
}
// Clear resource tide up by image
function Destroy() {
imagedestroy($this->img);
}
// Specify image format. Note depending on your installation
// of PHP not all formats may be supported.
function SetImgFormat($aFormat) {
$aFormat = strtolower($aFormat);
$tst = true;
$supported = imagetypes();
if( $aFormat=="auto" ) {
if( $supported & IMG_PNG )
$this->img_format="png";
elseif( $supported & IMG_JPG )
$this->img_format="jpeg";
elseif( $supported & IMG_GIF )
$this->img_format="gif";
else
JpGraphError::Raise(" Your PHP (and GD-lib) installation does not appear to support any known graphic formats.".
"You need to first make sure GD is compiled as a module to PHP. If you also want to use JPEG images".
"you must get the JPEG library. Please see the PHP docs for details.");
return true;
}
else {
if( $aFormat=="jpeg" || $aFormat=="png" || $aFormat=="gif" ) {
if( $aFormat=="jpeg" && !($supported & IMG_JPG) )
$tst=false;
elseif( $aFormat=="png" && !($supported & IMG_PNG) )
$tst=false;
elseif( $aFormat=="gif" && !($supported & IMG_GIF) )
$tst=false;
else {
$this->img_format=$aFormat;
return true;
}
}
else
$tst=false;
if( !$tst )
JpGraphError::Raise(" Your PHP installation does not support the chosen graphic format: $aFormat");
}
}
} // CLASS
 
//===================================================
// CLASS RotImage
// Description: Exactly as Image but draws the image at
// a specified angle around a specified rotation point.
//===================================================
class RotImage extends Image {
var $m=array();
var $a=0;
var $dx=0,$dy=0,$transx=0,$transy=0;
function RotImage($aWidth,$aHeight,$a=0,$aFormat=DEFAULT_GFORMAT) {
$this->Image($aWidth,$aHeight,$aFormat);
$this->dx=$this->left_margin+$this->plotwidth/2;
$this->dy=$this->top_margin+$this->plotheight/2;
$this->SetAngle($a);
}
function SetCenter($dx,$dy) {
$old_dx = $this->dx;
$old_dy = $this->dy;
$this->dx=$dx;
$this->dy=$dy;
$this->SetAngle($this->a);
return array($old_dx,$old_dy);
}
function SetTranslation($dx,$dy) {
$old = array($this->transx,$this->transy);
$this->transx = $dx;
$this->transy = $dy;
return $old;
}
 
function UpdateRotMatrice() {
$a = $this->a;
$a *= M_PI/180;
$sa=sin($a); $ca=cos($a);
// Create the rotation matrix
$this->m[0][0] = $ca;
$this->m[0][1] = -$sa;
$this->m[0][2] = $this->dx*(1-$ca) + $sa*$this->dy ;
$this->m[1][0] = $sa;
$this->m[1][1] = $ca;
$this->m[1][2] = $this->dy*(1-$ca) - $sa*$this->dx ;
}
 
function SetAngle($a) {
$tmp = $this->a;
$this->a = $a;
$this->UpdateRotMatrice();
return $tmp;
}
 
function Circle($xc,$yc,$r) {
// Circle get's rotated through the Arc() call
// made in the parent class
parent::Circle($xc,$yc,$r);
}
 
function FilledCircle($xc,$yc,$r) {
// If we use GD1 then Image::FilledCircle will use a
// call to Arc so it will get rotated through the Arc
// call.
if( $GLOBALS['gd2'] ) {
list($xc,$yc) = $this->Rotate($xc,$yc);
}
parent::FilledCircle($xc,$yc,$r);
}
 
function Arc($xc,$yc,$w,$h,$s,$e) {
list($xc,$yc) = $this->Rotate($xc,$yc);
$s += $this->a;
$e += $this->a;
parent::Arc($xc,$yc,$w,$h,$s,$e);
}
 
function FilledArc($xc,$yc,$w,$h,$s,$e) {
list($xc,$yc) = $this->Rotate($xc,$yc);
$s += $this->a;
$e += $this->a;
parent::FilledArc($xc,$yc,$w,$h,$s,$e);
}
 
function SetMargin($lm,$rm,$tm,$bm) {
parent::SetMargin($lm,$rm,$tm,$bm);
$this->dx=$this->left_margin+$this->plotwidth/2;
$this->dy=$this->top_margin+$this->plotheight/2;
$this->UpdateRotMatrice();
}
function Rotate($x,$y) {
// Optimization. Ignore rotation if Angle==0 || ANgle==360
if( $this->a == 0 || $this->a == 360 ) {
return array($x + $this->transx, $y + $this->transy );
}
else {
$x1=round($this->m[0][0]*$x + $this->m[0][1]*$y,1) + $this->m[0][2] + $this->transx;
$y1=round($this->m[1][0]*$x + $this->m[1][1]*$y,1) + $this->m[1][2] + $this->transy;
return array($x1,$y1);
}
}
function ArrRotate($pnts) {
for($i=0; $i < count($pnts)-1; $i+=2)
list($pnts[$i],$pnts[$i+1]) = $this->Rotate($pnts[$i],$pnts[$i+1]);
return $pnts;
}
function Line($x1,$y1,$x2,$y2) {
list($x1,$y1) = $this->Rotate($x1,$y1);
list($x2,$y2) = $this->Rotate($x2,$y2);
parent::Line($x1,$y1,$x2,$y2);
}
 
function Rectangle($x1,$y1,$x2,$y2) {
// Rectangle uses Line() so it will be rotated through that call
parent::Rectangle($x1,$y1,$x2,$y2);
}
function FilledRectangle($x1,$y1,$x2,$y2) {
if( $y1==$y2 || $x1==$x2 )
$this->Line($x1,$y1,$x2,$y2);
else
$this->FilledPolygon(array($x1,$y1,$x2,$y1,$x2,$y2,$x1,$y2));
}
function Polygon($pnts,$closed=FALSE) {
//Polygon uses Line() so it will be rotated through that call
parent::Polygon($pnts,$closed);
}
function FilledPolygon($pnts) {
parent::FilledPolygon($this->ArrRotate($pnts));
}
function Point($x,$y) {
list($xp,$yp) = $this->Rotate($x,$y);
parent::Point($xp,$yp);
}
function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
list($xp,$yp) = $this->Rotate($x,$y);
parent::StrokeText($xp,$yp,$txt,$dir,$paragraph_align,$debug);
}
}
 
//===================================================
// CLASS ImgStreamCache
// Description: Handle caching of graphs to files
//===================================================
class ImgStreamCache {
var $cache_dir;
var $img=null;
var $timeout=0; // Infinite timeout
//---------------
// CONSTRUCTOR
function ImgStreamCache(&$aImg, $aCacheDir=CACHE_DIR) {
$this->img = &$aImg;
$this->cache_dir = $aCacheDir;
}
 
//---------------
// PUBLIC METHODS
 
// Specify a timeout (in minutes) for the file. If the file is older then the
// timeout value it will be overwritten with a newer version.
// If timeout is set to 0 this is the same as infinite large timeout and if
// timeout is set to -1 this is the same as infinite small timeout
function SetTimeout($aTimeout) {
$this->timeout=$aTimeout;
}
// Output image to browser and also write it to the cache
function PutAndStream(&$aImage,$aCacheFileName,$aInline,$aStrokeFileName) {
// Some debugging code to brand the image with numbe of colors
// used
GLOBAL $gJpgBrandTiming;
if( $gJpgBrandTiming ) {
global $tim;
$t=$tim->Pop()/1000.0;
$c=$aImage->SetColor("black");
$t=sprintf(BRAND_TIME_FORMAT,round($t,3));
imagestring($this->img->img,2,5,$this->img->height-20,$t,$c);
}
 
// Check if we should stroke the image to an arbitrary file
if( $aStrokeFileName!="" ) {
if( $aStrokeFileName == "auto" )
$aStrokeFileName = GenImgName();
if( file_exists($aStrokeFileName) ) {
// Delete the old file
if( !@unlink($aStrokeFileName) )
JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
}
$aImage->Stream($aStrokeFileName);
return;
}
 
if( $aCacheFileName != "" && USE_CACHE) {
 
$aCacheFileName = $this->cache_dir . $aCacheFileName;
if( file_exists($aCacheFileName) ) {
if( !$aInline ) {
// If we are generating image off-line (just writing to the cache)
// and the file exists and is still valid (no timeout)
// then do nothing, just return.
$diff=time()-filemtime($aCacheFileName);
if( $diff < 0 )
JpGraphError::Raise(" Cached imagefile ($aCacheFileName) has file date in the future!!");
if( $this->timeout>0 && ($diff <= $this->timeout*60) )
return;
}
if( !@unlink($aCacheFileName) )
JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
$aImage->Stream($aCacheFileName);
}
else {
$this->MakeDirs(dirname($aCacheFileName));
if( !is_writeable(dirname($aCacheFileName)) ) {
JpGraphError::Raise('PHP has not enough permissions to write to the cache file '.$aCacheFileName.'. Please make sure that the user running PHP has write permission for this file if you wan to use the cache system with JpGraph.');
}
$aImage->Stream($aCacheFileName);
}
$res=true;
// Set group to specified
if( CACHE_FILE_GROUP != "" )
$res = @chgrp($aCacheFileName,CACHE_FILE_GROUP);
if( CACHE_FILE_MOD != "" )
$res = @chmod($aCacheFileName,CACHE_FILE_MOD);
if( !$res )
JpGraphError::Raise(" Can't set permission for cached image $aStrokeFileName. Permission problem?");
$aImage->Destroy();
if( $aInline ) {
if ($fh = @fopen($aCacheFileName, "rb") ) {
$this->img->Headers();
fpassthru($fh);
return;
}
else
JpGraphError::Raise(" Cant open file from cache [$aFile]");
}
}
elseif( $aInline ) {
$this->img->Headers();
$aImage->Stream();
return;
}
}
// Check if a given image is in cache and in that case
// pass it directly on to web browser. Return false if the
// image file doesn't exist or exists but is to old
function GetAndStream($aCacheFileName) {
$aCacheFileName = $this->cache_dir.$aCacheFileName;
if ( USE_CACHE && file_exists($aCacheFileName) && $this->timeout>=0 ) {
$diff=time()-filemtime($aCacheFileName);
if( $this->timeout>0 && ($diff > $this->timeout*60) ) {
return false;
}
else {
if ($fh = @fopen($aCacheFileName, "rb")) {
$this->img->Headers();
fpassthru($fh);
return true;
}
else
JpGraphError::Raise(" Can't open cached image \"$aCacheFileName\" for reading.");
}
}
return false;
}
//---------------
// PRIVATE METHODS
// Create all necessary directories in a path
function MakeDirs($aFile) {
$dirs = array();
while ( !(file_exists($aFile)) ) {
$dirs[] = $aFile;
$aFile = dirname($aFile);
}
for ($i = sizeof($dirs)-1; $i>=0; $i--) {
if(! @mkdir($dirs[$i],0777) )
JpGraphError::Raise(" Can't create directory $aFile. Make sure PHP has write permission to this directory.");
// We also specify mode here after we have changed group.
// This is necessary if Apache user doesn't belong the
// default group and hence can't specify group permission
// in the previous mkdir() call
if( CACHE_FILE_GROUP != "" ) {
$res=true;
$res =@chgrp($dirs[$i],CACHE_FILE_GROUP);
$res &= @chmod($dirs[$i],0777);
if( !$res )
JpGraphError::Raise(" Can't set permissions for $aFile. Permission problems?");
}
}
return true;
}
} // CLASS Cache
//===================================================
// CLASS Legend
// Description: Responsible for drawing the box containing
// all the legend text for the graph
//===================================================
DEFINE('_DEFAULT_LPM_SIZE',8);
class Legend {
var $color=array(0,0,0); // Default fram color
var $fill_color=array(235,235,235); // Default fill color
var $shadow=true; // Shadow around legend "box"
var $shadow_color='gray';
var $txtcol=array();
var $mark_abs_size=_DEFAULT_LPM_SIZE;
var $xmargin=10,$ymargin=6,$shadow_width=2;
var $xpos=0.05, $ypos=0.15, $xabspos=-1, $yabspos=-1;
var $halign="right", $valign="top";
var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
var $font_color='black';
var $hide=false,$layout_n=1;
var $weight=1,$frameweight=1;
var $csimareas='';
//---------------
// CONSTRUCTOR
function Legend() {
// Empty
}
//---------------
// PUBLIC METHODS
function Hide($aHide=true) {
$this->hide=$aHide;
}
function SetShadow($aShow='gray',$aWidth=2) {
if( is_string($aShow) ) {
$this->shadow_color = $aShow;
$this->shadow=true;
}
else
$this->shadow=$aShow;
$this->shadow_width=$aWidth;
}
 
function SetMarkAbsSize($aSize) {
$this->mark_abs_size = $aSize ;
}
 
function SetLineWeight($aWeight) {
$this->weight = $aWeight;
}
 
function SetFrameWeight($aWeight) {
$this->frameweight = $aWeight;
}
function SetLayout($aDirection=LEGEND_VERT) {
$this->layout_n = $aDirection==LEGEND_VERT ? 1 : 99 ;
}
function SetColumns($aCols) {
$this->layout_n = $aCols ;
}
 
function SetLineSpacing($aSpacing) {
$this->ymargin = $aSpacing ;
}
 
// Set color on frame around box
function SetColor($aFontColor,$aColor='black') {
$this->font_color=$aFontColor;
$this->color=$aColor;
}
function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
$this->font_family = $aFamily;
$this->font_style = $aStyle;
$this->font_size = $aSize;
}
function SetPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
$this->Pos($aX,$aY,$aHAlign,$aVAlign);
}
 
function SetAbsPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
$this->xabspos=$aX;
$this->yabspos=$aY;
$this->halign=$aHAlign;
$this->valign=$aVAlign;
}
 
 
function Pos($aX,$aY,$aHAlign="right",$aVAlign="top") {
if( !($aX<1 && $aY<1) )
JpGraphError::Raise(" Position for legend must be given as percentage in range 0-1");
$this->xpos=$aX;
$this->ypos=$aY;
$this->halign=$aHAlign;
$this->valign=$aVAlign;
}
 
function SetFillColor($aColor) {
$this->fill_color=$aColor;
}
function Add($aTxt,$aColor,$aPlotmark="",$aLinestyle=0,$csimtarget="",$csimalt="") {
$this->txtcol[]=array($aTxt,$aColor,$aPlotmark,$aLinestyle,$csimtarget,$csimalt);
}
 
function GetCSIMAreas() {
return $this->csimareas;
}
function Stroke(&$aImg) {
if( $this->hide ) return;
 
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
 
$n=count($this->txtcol);
if( $n == 0 ) return;
 
// Find out the max width and height of each column to be able
// to size the legend box.
$numcolumns = ($n > $this->layout_n ? $this->layout_n : $n);
for( $i=0; $i < $numcolumns; ++$i ) {
$colwidth[$i] = $aImg->GetTextWidth($this->txtcol[$i][0]) +
2*$this->xmargin + 2*$this->mark_abs_size;
$colheight[$i] = 0;
}
 
// Find our maximum height in each row
$rows = -1 ;
for( $i=0; $i < $n; ++$i ) {
$h = max($this->mark_abs_size,$aImg->GetTextHeight($this->txtcol[$i][0]))+$this->ymargin;
if( $i % $numcolumns == 0 ) {
$rows++;
$rowheight[$rows] = 0;
}
$rowheight[$rows] = max($rowheight[$rows],$h);
}
 
$abs_height = 0;
for( $i=0; $i <= $rows; ++$i ) {
$abs_height += $rowheight[$i] ;
}
 
// Make damn sure that the height is at least as high as mark size + ymargin
$abs_height = max($abs_height,$this->mark_abs_size+$this->ymargin);
$abs_height += 2*$this->ymargin;
// Find out the maximum width in each column
for( $i=$numcolumns; $i < $n; ++$i ) {
$colwidth[$i % $numcolumns] = max(
$aImg->GetTextWidth($this->txtcol[$i][0])+2*$this->xmargin+2*$this->mark_abs_size,
$colwidth[$i % $numcolumns]);
}
// Get the total width
$mtw = 0;
for( $i=0; $i < $numcolumns; ++$i ) {
$mtw += $colwidth[$i];
}
 
// Find out maximum width we need for legend box
$abs_width = $mtw+$this->xmargin;
 
if( $this->xabspos === -1 && $this->yabspos === -1 ) {
$this->xabspos = $this->xpos*$aImg->width ;
$this->yabspos = $this->ypos*$aImg->height ;
}
 
// Positioning of the legend box
if( $this->halign=="left" )
$xp = $this->xabspos;
elseif( $this->halign=="center" )
$xp = $this->xabspos - $abs_width/2;
else
$xp = $aImg->width - $this->xabspos - $abs_width;
 
$yp=$this->yabspos;
if( $this->valign=="center" )
$yp-=$abs_height/2;
elseif( $this->valign=="bottom" )
$yp-=$abs_height;
// Stroke legend box
$aImg->SetColor($this->color);
$aImg->SetLineWeight($this->frameweight);
 
if( $this->shadow )
$aImg->ShadowRectangle($xp,$yp,$xp+$abs_width+$this->shadow_width,
$yp+$abs_height+$this->shadow_width,
$this->fill_color,$this->shadow_width,$this->shadow_color);
else {
$aImg->SetColor($this->fill_color);
$aImg->FilledRectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
$aImg->SetColor($this->color);
$aImg->Rectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
}
 
// x1,y1 is the position for the legend mark
$aImg->SetLineWeight($this->weight);
$x1=$xp+$this->mark_abs_size/2+3;
$y1=$yp + $this->mark_abs_size/2 + $this->ymargin/2;
$f2 = round($aImg->GetTextHeight('X')/2);
 
$grad = new Gradient($aImg);
 
// Now stroke each legend in turn
$i = 1 ; $row = 0;
foreach($this->txtcol as $p) {
$x1 = round($x1); $y1=round($y1);
if ( $p[2] != "" && $p[2]->GetType() > -1 ) {
// Make a plot mark legend
$aImg->SetColor($p[1]);
if( $p[3] > 0 ) {
$aImg->SetLineStyle($p[3]);
$aImg->StyleLine($x1-3,$y1+$f2,$x1+$this->mark_abs_size+3,$y1+$f2);
}
// Stroke a mark with the standard size
// (As long as it is not an image mark )
if( $p[2]->GetType() != MARK_IMG ) {
$p[2]->iFormatCallback = '';
if( $this->mark_abs_size === _DEFAULT_LPM_SIZE )
$p[2]->SetDefaultWidth();
else
$p[2]->SetSize($this->mark_abs_size);
$p[2]->Stroke($aImg,$x1+$this->mark_abs_size/2,$y1+$f2);
}
}
elseif ( $p[2] != "" && (is_string($p[3]) || $p[3]>0 ) ) {
// Draw a styled line
$aImg->SetColor($p[1]);
$aImg->SetLineStyle($p[3]);
$aImg->StyleLine($x1,$y1+$f2,$x1+$this->mark_abs_size,$y1+$f2);
$aImg->StyleLine($x1,$y1+$f2+1,$x1+$this->mark_abs_size,$y1+$f2+1);
}
else {
// Draw a colored box
$color = $p[1] ;
$ym = round($y1 + $f2 - $this->mark_abs_size/2);
if( is_array($color) && count($color)==3 ) {
// The client want a gradient color
$grad->FilledRectangle($x1,$ym,
$x1+$this->mark_abs_size,$ym+$this->mark_abs_size,
$color[0],$color[1],$color[2]);
}
else {
$aImg->SetColor($p[1]);
$aImg->FilledRectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
}
$aImg->SetColor($this->color);
$aImg->SetLineWeight($this->weight);
$aImg->Rectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
}
$aImg->SetColor($this->font_color);
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
$aImg->SetTextAlign("left","top");
$aImg->StrokeText(round($x1+$this->mark_abs_size+$this->xmargin*1.5),$y1,$p[0]);
 
// Add CSIM for Legend if defined
if( $p[4] != "" ) {
$xe = $x1 + $this->xmargin+2*$this->mark_abs_size+$aImg->GetTextWidth($p[0]);
$ye = $y1 + max($this->mark_abs_size,$aImg->GetTextHeight($p[0]));
$coords = "$x1,$y1,$xe,$y1,$xe,$ye,$x1,$ye";
$this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$p[4]."\"";
if( !empty($p[5]) ) {
$tmp=sprintf($p[5],$p[0]);
$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
}
$this->csimareas .= ">\n";
}
if( $i >= $this->layout_n ) {
$x1 = $xp+$this->mark_abs_size/2+3;
//$y1 += max($aImg->GetTextHeight($p[0]),$this->mark_abs_size)+$this->ymargin;
$y1 += $rowheight[$row++];
$i = 1;
}
else {
$x1 += $colwidth[($i-1) % $numcolumns] ;
++$i;
}
}
}
} // Class
 
//===================================================
// CLASS DisplayValue
// Description: Used to print data values at data points
//===================================================
class DisplayValue {
var $show=false,$format="%.1f",$negformat="";
var $iFormCallback='';
var $angle=0;
var $ff=FF_FONT1,$fs=FS_NORMAL,$fsize=10;
var $color="navy",$negcolor="";
var $margin=5,$valign="",$halign="center";
var $iHideZero=false;
 
function Show($aFlag=true) {
$this->show=$aFlag;
}
 
function SetColor($aColor,$aNegcolor="") {
$this->color = $aColor;
$this->negcolor = $aNegcolor;
}
 
function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
$this->ff=$aFontFamily;
$this->fs=$aFontStyle;
$this->fsize=$aFontSize;
}
 
function SetMargin($aMargin) {
$this->margin = $aMargin;
}
 
function SetAngle($aAngle) {
$this->angle = $aAngle;
}
 
function SetAlign($aHAlign,$aVAlign='') {
$this->halign = $aHAlign;
$this->valign = $aVAlign;
}
 
function SetFormat($aFormat,$aNegFormat="") {
$this->format= $aFormat;
$this->negformat= $aNegFormat;
}
 
function SetFormatCallback($aFunc) {
$this->iFormCallback = $aFunc;
}
 
function HideZero($aFlag=true) {
$this->iHideZero=$aFlag;
}
 
function Stroke($img,$aVal,$x,$y) {
if( $this->show )
{
if( $this->negformat=="" ) $this->negformat=$this->format;
if( $this->negcolor=="" ) $this->negcolor=$this->color;
 
if( $aVal===NULL || (is_string($aVal) && ($aVal=="" || $aVal=="-" || $aVal=="x" ) ) )
return;
 
if( is_numeric($aVal) && $aVal==0 && $this->iHideZero ) {
return;
}
 
// Since the value is used in different cirumstances we need to check what
// kind of formatting we shall use. For example, to display values in a line
// graph we simply display the formatted value, but in the case where the user
// has already specified a text string we don't fo anything.
if( $this->iFormCallback != '' ) {
$f = $this->iFormCallback;
$sval = $f($aVal);
}
elseif( is_numeric($aVal) ) {
if( $aVal >= 0 )
$sval=sprintf($this->format,$aVal);
else
$sval=sprintf($this->negformat,$aVal);
}
else
$sval=$aVal;
 
 
$y = $y-sign($aVal)*$this->margin;
 
$txt = new Text($sval,$x,$y);
$txt->SetFont($this->ff,$this->fs,$this->fsize);
if( $this->valign == "" ) {
if( $aVal >= 0 )
$valign = "bottom";
else
$valign = "top";
}
else
$valign = $this->valign;
$txt->Align($this->halign,$valign);
 
$txt->SetOrientation($this->angle);
if( $aVal > 0 )
$txt->SetColor($this->color);
else
$txt->SetColor($this->negcolor);
$txt->Stroke($img);
}
}
}
 
 
//===================================================
// CLASS Plot
// Description: Abstract base class for all concrete plot classes
//===================================================
class Plot {
var $line_weight=1;
var $coords=array();
var $legend='',$hidelegend=false;
var $csimtargets=array(); // Array of targets for CSIM
var $csimareas=""; // Resultant CSIM area tags
var $csimalts=null; // ALT:s for corresponding target
var $color="black";
var $numpoints=0;
var $weight=1;
var $value;
var $center=false;
var $legendcsimtarget='';
var $legendcsimalt='';
//---------------
// CONSTRUCTOR
function Plot(&$aDatay,$aDatax=false) {
$this->numpoints = count($aDatay);
if( $this->numpoints==0 )
JpGraphError::Raise(" Empty data array specified for plot. Must have at least one data point.");
$this->coords[0]=$aDatay;
if( is_array($aDatax) )
$this->coords[1]=$aDatax;
$this->value = new DisplayValue();
}
 
//---------------
// PUBLIC METHODS
 
// Stroke the plot
// "virtual" function which must be implemented by
// the subclasses
function Stroke(&$aImg,&$aXScale,&$aYScale) {
JpGraphError::Raise("JpGraph: Stroke() must be implemented by concrete subclass to class Plot");
}
 
function HideLegend($f=true) {
$this->hidelegend = $f;
}
 
function DoLegend(&$graph) {
if( !$this->hidelegend )
$this->Legend($graph);
}
 
function StrokeDataValue($img,$aVal,$x,$y) {
$this->value->Stroke($img,$aVal,$x,$y);
}
// Set href targets for CSIM
function SetCSIMTargets($aTargets,$aAlts=null) {
$this->csimtargets=$aTargets;
$this->csimalts=$aAlts;
}
// Get all created areas
function GetCSIMareas() {
return $this->csimareas;
}
// "Virtual" function which gets called before any scale
// or axis are stroked used to do any plot specific adjustment
function PreStrokeAdjust(&$aGraph) {
if( substr($aGraph->axtype,0,4) == "text" && (isset($this->coords[1])) )
JpGraphError::Raise("JpGraph: You can't use a text X-scale with specified X-coords. Use a \"int\" or \"lin\" scale instead.");
return true;
}
// Get minimum values in plot
function Min() {
if( isset($this->coords[1]) )
$x=$this->coords[1];
else
$x="";
if( $x != "" && count($x) > 0 )
$xm=min($x);
else
$xm=0;
$y=$this->coords[0];
if( count($y) > 0 ) {
$ym = $y[0];
$cnt = count($y);
$i=0;
while( $i<$cnt && !is_numeric($ym=$y[$i]) )
$i++;
while( $i < $cnt) {
if( is_numeric($y[$i]) )
$ym=min($ym,$y[$i]);
++$i;
}
}
else
$ym="";
return array($xm,$ym);
}
// Get maximum value in plot
function Max() {
if( isset($this->coords[1]) )
$x=$this->coords[1];
else
$x="";
 
if( $x!="" && count($x) > 0 )
$xm=max($x);
else {
//$xm=count($this->coords[0])-1; // We count from 0..(n-1)
$xm = $this->numpoints-1;
}
$y=$this->coords[0];
if( count($y) > 0 ) {
if( !isset($y[0]) ) {
$y[0] = 0;
// Change in 1.5.1 Don't treat this as an error any more. Just silently concert to 0
// JpGraphError::Raise(" You have not specified a y[0] value!!");
}
$cnt = count($y);
$i=0;
while( $i<$cnt && !is_numeric($ym=$y[$i]) )
$i++;
while( $i < $cnt ) {
if( is_numeric($y[$i]) ) $ym=max($ym,$y[$i]);
++$i;
}
}
else
$ym="";
return array($xm,$ym);
}
function SetColor($aColor) {
$this->color=$aColor;
}
function SetLegend($aLegend,$aCSIM="",$aCSIMAlt="") {
$this->legend = $aLegend;
$this->legendcsimtarget = $aCSIM;
$this->legendcsimalt = $aCSIMAlt;
}
 
function SetWeight($aWeight) {
$this->weight=$aWeight;
}
function SetLineWeight($aWeight=1) {
$this->line_weight=$aWeight;
}
function SetCenter($aCenter=true) {
$this->center = $aCenter;
}
// This method gets called by Graph class to plot anything that should go
// into the margin after the margin color has been set.
function StrokeMargin(&$aImg) {
return true;
}
 
// Framework function the chance for each plot class to set a legend
function Legend(&$aGraph) {
if( $this->legend != "" )
$aGraph->legend->Add($this->legend,$this->color,"",0,$this->legendcsimtarget,$this->legendcsimalt);
}
} // Class
 
require_once "jpgraph_plotmark.inc" ;
 
//==============================================================================
// The following section contains classes to implement the "band" functionality
//==============================================================================
 
// Utility class to hold coordinates for a rectangle
class Rectangle {
var $x,$y,$w,$h;
var $xe, $ye;
function Rectangle($aX,$aY,$aWidth,$aHeight) {
$this->x=$aX;
$this->y=$aY;
$this->w=$aWidth;
$this->h=$aHeight;
$this->xe=$aX+$aWidth-1;
$this->ye=$aY+$aHeight-1;
}
}
 
//=====================================================================
// Class RectPattern
// Base class for pattern hierarchi that is used to display patterned
// bands on the graph. Any subclass that doesn't override Stroke()
// must at least implement method DoPattern(&$aImg) which is responsible
// for drawing the pattern onto the graph.
//=====================================================================
class RectPattern {
var $color;
var $weight;
var $rect=null;
var $doframe=true;
var $linespacing; // Line spacing in pixels
var $iBackgroundColor=-1; // Default is no background fill
function RectPattern($aColor,$aWeight=1) {
$this->color = $aColor;
$this->weight = $aWeight;
}
 
function SetBackground($aBackgroundColor) {
$this->iBackgroundColor=$aBackgroundColor;
}
 
function SetPos(&$aRect) {
$this->rect = $aRect;
}
function ShowFrame($aShow=true) {
$this->doframe=$aShow;
}
 
function SetDensity($aDens) {
if( $aDens <1 || $aDens > 100 )
JpGraphError::Raise(" Desity for pattern must be between 1 and 100. (You tried $aDens)");
// 1% corresponds to linespacing=50
// 100 % corresponds to linespacing 1
$this->linespacing = floor(((100-$aDens)/100.0)*50)+1;
 
}
 
function Stroke(&$aImg) {
if( $this->rect == null )
JpGraphError::Raise(" No positions specified for pattern.");
 
if( !(is_numeric($this->iBackgroundColor) && $this->iBackgroundColor==-1) ) {
$aImg->SetColor($this->iBackgroundColor);
$aImg->FilledRectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
}
 
$aImg->SetColor($this->color);
$aImg->SetLineWeight($this->weight);
 
// Virtual function implemented by subclass
$this->DoPattern($aImg);
 
// Frame around the pattern area
if( $this->doframe )
$aImg->Rectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
}
 
}
 
 
//=====================================================================
// Class RectPatternSolid
// Implements a solid band
//=====================================================================
class RectPatternSolid extends RectPattern {
 
function RectPatternSolid($aColor="black",$aWeight=1) {
parent::RectPattern($aColor,$aWeight);
}
 
function DoPattern(&$aImg) {
$aImg->SetColor($this->color);
$aImg->FilledRectangle($this->rect->x,$this->rect->y,
$this->rect->xe,$this->rect->ye);
}
}
 
//=====================================================================
// Class RectPatternHor
// Implements horizontal line pattern
//=====================================================================
class RectPatternHor extends RectPattern {
function RectPatternHor($aColor="black",$aWeight=1,$aLineSpacing=7) {
parent::RectPattern($aColor,$aWeight);
$this->linespacing = $aLineSpacing;
}
function DoPattern(&$aImg) {
$x0 = $this->rect->x;
$x1 = $this->rect->xe;
$y = $this->rect->y;
while( $y < $this->rect->ye ) {
$aImg->Line($x0,$y,$x1,$y);
$y += $this->linespacing;
}
}
}
 
//=====================================================================
// Class RectPatternVert
// Implements vertical line pattern
//=====================================================================
class RectPatternVert extends RectPattern {
var $linespacing=10; // Line spacing in pixels
function RectPatternVert($aColor="black",$aWeight=1,$aLineSpacing=7) {
parent::RectPattern($aColor,$aWeight);
$this->linespacing = $aLineSpacing;
}
 
//--------------------
// Private methods
//
function DoPattern(&$aImg) {
$x = $this->rect->x;
$y0 = $this->rect->y;
$y1 = $this->rect->ye;
while( $x < $this->rect->xe ) {
$aImg->Line($x,$y0,$x,$y1);
$x += $this->linespacing;
}
}
}
 
 
//=====================================================================
// Class RectPatternRDiag
// Implements right diagonal pattern
//=====================================================================
class RectPatternRDiag extends RectPattern {
var $linespacing; // Line spacing in pixels
function RectPatternRDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
parent::RectPattern($aColor,$aWeight);
$this->linespacing = $aLineSpacing;
}
 
function DoPattern(&$aImg) {
// --------------------
// | / / / / /|
// |/ / / / / |
// | / / / / |
// --------------------
$xe = $this->rect->xe;
$ye = $this->rect->ye;
$x0 = $this->rect->x + round($this->linespacing/2);
$y0 = $this->rect->y;
$x1 = $this->rect->x;
$y1 = $this->rect->y + round($this->linespacing/2);
 
while($x0<=$xe && $y1<=$ye) {
$aImg->Line($x0,$y0,$x1,$y1);
$x0 += $this->linespacing;
$y1 += $this->linespacing;
}
 
if( $xe-$x1 > $ye-$y0 ) {
// Width larger than height
$x1 = $this->rect->x + ($y1-$ye);
$y1 = $ye;
$y0 = $this->rect->y;
while( $x0 <= $xe ) {
$aImg->Line($x0,$y0,$x1,$y1);
$x0 += $this->linespacing;
$x1 += $this->linespacing;
}
$y0=$this->rect->y + ($x0-$xe);
$x0=$xe;
}
else {
// Height larger than width
$diff = $x0-$xe;
$y0 = $diff+$this->rect->y;
$x0 = $xe;
$x1 = $this->rect->x;
while( $y1 <= $ye ) {
$aImg->Line($x0,$y0,$x1,$y1);
$y1 += $this->linespacing;
$y0 += $this->linespacing;
}
$diff = $y1-$ye;
$y1 = $ye;
$x1 = $diff + $this->rect->x;
}
 
while( $y0 <= $ye ) {
$aImg->Line($x0,$y0,$x1,$y1);
$y0 += $this->linespacing;
$x1 += $this->linespacing;
}
}
}
//=====================================================================
// Class RectPatternLDiag
// Implements left diagonal pattern
//=====================================================================
class RectPatternLDiag extends RectPattern {
var $linespacing; // Line spacing in pixels
function RectPatternLDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
$this->linespacing = $aLineSpacing;
parent::RectPattern($aColor,$aWeight);
}
 
function DoPattern(&$aImg) {
// --------------------
// |\ \ \ \ \ |
// | \ \ \ \ \|
// | \ \ \ \ |
// |------------------|
$xe = $this->rect->xe;
$ye = $this->rect->ye;
$x0 = $this->rect->x + round($this->linespacing/2);
$y0 = $this->rect->ye;
$x1 = $this->rect->x;
$y1 = $this->rect->ye - round($this->linespacing/2);
 
while($x0<=$xe && $y1>=$this->rect->y) {
$aImg->Line($x0,$y0,$x1,$y1);
$x0 += $this->linespacing;
$y1 -= $this->linespacing;
}
if( $xe-$x1 > $ye-$this->rect->y ) {
// Width larger than height
$x1 = $this->rect->x + ($this->rect->y-$y1);
$y0=$ye; $y1=$this->rect->y;
while( $x0 <= $xe ) {
$aImg->Line($x0,$y0,$x1,$y1);
$x0 += $this->linespacing;
$x1 += $this->linespacing;
}
$y0=$this->rect->ye - ($x0-$xe);
$x0=$xe;
}
else {
// Height larger than width
$diff = $x0-$xe;
$y0 = $ye-$diff;
$x0 = $xe;
while( $y1 >= $this->rect->y ) {
$aImg->Line($x0,$y0,$x1,$y1);
$y0 -= $this->linespacing;
$y1 -= $this->linespacing;
}
$diff = $this->rect->y - $y1;
$x1 = $this->rect->x + $diff;
$y1 = $this->rect->y;
}
while( $y0 >= $this->rect->y ) {
$aImg->Line($x0,$y0,$x1,$y1);
$y0 -= $this->linespacing;
$x1 += $this->linespacing;
}
}
}
 
//=====================================================================
// Class RectPattern3DPlane
// Implements "3D" plane pattern
//=====================================================================
class RectPattern3DPlane extends RectPattern {
var $alpha=50; // Parameter that specifies the distance
// to "simulated" horizon in pixel from the
// top of the band. Specifies how fast the lines
// converge.
 
function RectPattern3DPlane($aColor="black",$aWeight=1) {
parent::RectPattern($aColor,$aWeight);
$this->SetDensity(10); // Slightly larger default
}
 
function SetHorizon($aHorizon) {
$this->alpha=$aHorizon;
}
function DoPattern(&$aImg) {
// "Fake" a nice 3D grid-effect.
$x0 = $this->rect->x + $this->rect->w/2;
$y0 = $this->rect->y;
$x1 = $x0;
$y1 = $this->rect->ye;
$x0_right = $x0;
$x1_right = $x1;
 
// BTW "apa" means monkey in Swedish but is really a shortform for
// "alpha+a" which was the labels I used on paper when I derived the
// geometric to get the 3D perspective right.
// $apa is the height of the bounding rectangle plus the distance to the
// artifical horizon (alpha)
$apa = $this->rect->h + $this->alpha;
 
// Three cases and three loops
// 1) The endpoint of the line ends on the bottom line
// 2) The endpoint ends on the side
// 3) Horizontal lines
 
// Endpoint falls on bottom line
$middle=$this->rect->x + $this->rect->w/2;
$dist=$this->linespacing;
$factor=$this->alpha /($apa);
while($x1>$this->rect->x) {
$aImg->Line($x0,$y0,$x1,$y1);
$aImg->Line($x0_right,$y0,$x1_right,$y1);
$x1 = $middle - $dist;
$x0 = $middle - $dist * $factor;
$x1_right = $middle + $dist;
$x0_right = $middle + $dist * $factor;
$dist += $this->linespacing;
}
 
// Endpoint falls on sides
$dist -= $this->linespacing;
$d=$this->rect->w/2;
$c = $apa - $d*$apa/$dist;
while( $x0>$this->rect->x ) {
$aImg->Line($x0,$y0,$this->rect->x,$this->rect->ye-$c);
$aImg->Line($x0_right,$y0,$this->rect->xe,$this->rect->ye-$c);
$dist += $this->linespacing;
$x0 = $middle - $dist * $factor;
$x1 = $middle - $dist;
$x0_right = $middle + $dist * $factor;
$c = $apa - $d*$apa/$dist;
}
// Horizontal lines
// They need some serious consideration since they are a function
// of perspective depth (alpha) and density (linespacing)
$x0=$this->rect->x;
$x1=$this->rect->xe;
$y=$this->rect->ye;
// The first line is drawn directly. Makes the loop below slightly
// more readable.
$aImg->Line($x0,$y,$x1,$y);
$hls = $this->linespacing;
// A correction factor for vertical "brick" line spacing to account for
// a) the difference in number of pixels hor vs vert
// b) visual apperance to make the first layer of "bricks" look more
// square.
$vls = $this->linespacing*0.6;
$ds = $hls*($apa-$vls)/$apa;
// Get the slope for the "perspective line" going from bottom right
// corner to top left corner of the "first" brick.
// Uncomment the following lines if you want to get a visual understanding
// of what this helpline does. BTW this mimics the way you would get the
// perspective right when drawing on paper.
/*
$x0 = $middle;
$y0 = $this->rect->ye;
$len=floor(($this->rect->ye-$this->rect->y)/$vls);
$x1 = $middle-round($len*$ds);
$y1 = $this->rect->ye-$len*$vls;
$aImg->PushColor("red");
$aImg->Line($x0,$y0,$x1,$y1);
$aImg->PopColor();
*/
$y -= $vls;
$k=($this->rect->ye-($this->rect->ye-$vls))/($middle-($middle-$ds));
$dist = $hls;
while( $y>$this->rect->y ) {
$aImg->Line($this->rect->x,$y,$this->rect->xe,$y);
$adj = $k*$dist/(1+$dist*$k/$apa);
if( $adj < 2 ) $adj=2;
$y = $this->rect->ye - round($adj);
$dist += $hls;
}
}
}
 
//=====================================================================
// Class RectPatternCross
// Vert/Hor crosses
//=====================================================================
class RectPatternCross extends RectPattern {
var $vert=null;
var $hor=null;
function RectPatternCross($aColor="black",$aWeight=1) {
parent::RectPattern($aColor,$aWeight);
$this->vert = new RectPatternVert($aColor,$aWeight);
$this->hor = new RectPatternHor($aColor,$aWeight);
}
 
function SetOrder($aDepth) {
$this->vert->SetOrder($aDepth);
$this->hor->SetOrder($aDepth);
}
 
function SetPos(&$aRect) {
parent::SetPos($aRect);
$this->vert->SetPos($aRect);
$this->hor->SetPos($aRect);
}
 
function SetDensity($aDens) {
$this->vert->SetDensity($aDens);
$this->hor->SetDensity($aDens);
}
 
function DoPattern(&$aImg) {
$this->vert->DoPattern($aImg);
$this->hor->DoPattern($aImg);
}
}
 
//=====================================================================
// Class RectPatternDiagCross
// Vert/Hor crosses
//=====================================================================
 
class RectPatternDiagCross extends RectPattern {
var $left=null;
var $right=null;
function RectPatternDiagCross($aColor="black",$aWeight=1) {
parent::RectPattern($aColor,$aWeight);
$this->right = new RectPatternRDiag($aColor,$aWeight);
$this->left = new RectPatternLDiag($aColor,$aWeight);
}
 
function SetOrder($aDepth) {
$this->left->SetOrder($aDepth);
$this->right->SetOrder($aDepth);
}
 
function SetPos(&$aRect) {
parent::SetPos($aRect);
$this->left->SetPos($aRect);
$this->right->SetPos($aRect);
}
 
function SetDensity($aDens) {
$this->left->SetDensity($aDens);
$this->right->SetDensity($aDens);
}
 
function DoPattern(&$aImg) {
$this->left->DoPattern($aImg);
$this->right->DoPattern($aImg);
}
 
}
 
//=====================================================================
// Class RectPatternFactory
// Factory class for rectangular pattern
//=====================================================================
class RectPatternFactory {
function RectPatternFactory() {
// Empty
}
function Create($aPattern,$aColor,$aWeight=1) {
switch($aPattern) {
case BAND_RDIAG:
$obj = new RectPatternRDiag($aColor,$aWeight);
break;
case BAND_LDIAG:
$obj = new RectPatternLDiag($aColor,$aWeight);
break;
case BAND_SOLID:
$obj = new RectPatternSolid($aColor,$aWeight);
break;
case BAND_VLINE:
$obj = new RectPatternVert($aColor,$aWeight);
break;
case BAND_HLINE:
$obj = new RectPatternHor($aColor,$aWeight);
break;
case BAND_3DPLANE:
$obj = new RectPattern3DPlane($aColor,$aWeight);
break;
case BAND_HVCROSS:
$obj = new RectPatternCross($aColor,$aWeight);
break;
case BAND_DIAGCROSS:
$obj = new RectPatternDiagCross($aColor,$aWeight);
break;
default:
JpGraphError::Raise(" Unknown pattern specification ($aPattern)");
}
return $obj;
}
}
 
 
//=====================================================================
// Class PlotBand
// Factory class which is used by the client.
// It is reposnsible for factoring the corresponding pattern
// concrete class.
//=====================================================================
class PlotBand {
var $prect=null;
var $depth;
var $dir, $min, $max;
 
function PlotBand($aDir,$aPattern,$aMin,$aMax,$aColor="black",$aWeight=1,$aDepth=DEPTH_BACK) {
$f = new RectPatternFactory();
$this->prect = $f->Create($aPattern,$aColor,$aWeight);
$this->dir = $aDir;
$this->min = $aMin;
$this->max = $aMax;
$this->depth=$aDepth;
}
// Set position. aRect contains absolute image coordinates
function SetPos(&$aRect) {
assert( $this->prect != null ) ;
$this->prect->SetPos($aRect);
}
function ShowFrame($aFlag=true) {
$this->prect->ShowFrame($aFlag);
}
 
// Set z-order. In front of pplot or in the back
function SetOrder($aDepth) {
$this->depth=$aDepth;
}
function SetDensity($aDens) {
$this->prect->SetDensity($aDens);
}
function GetDir() {
return $this->dir;
}
function GetMin() {
return $this->min;
}
function GetMax() {
return $this->max;
}
// Display band
function Stroke(&$aImg,&$aXScale,&$aYScale) {
assert( $this->prect != null ) ;
if( $this->dir == HORIZONTAL ) {
if( $this->min === 'min' ) $this->min = $aYScale->GetMinVal();
if( $this->max === 'max' ) $this->max = $aYScale->GetMaxVal();
 
// Only draw the bar if it actually appears in the range
if ($this->min < $aYScale->GetMaxVal() && $this->max > $aYScale->GetMinVal()) {
// Trucate to limit of axis
$this->min = max($this->min, $aYScale->GetMinVal());
$this->max = min($this->max, $aYScale->GetMaxVal());
 
$x=$aXScale->scale_abs[0];
$y=$aYScale->Translate($this->max);
$width=$aXScale->scale_abs[1]-$aXScale->scale_abs[0]+1;
$height=abs($y-$aYScale->Translate($this->min))+1;
$this->prect->SetPos(new Rectangle($x,$y,$width,$height));
$this->prect->Stroke($aImg);
}
}
else { // VERTICAL
if( $this->min === 'min' ) $this->min = $aXScale->GetMinVal();
if( $this->max === 'max' ) $this->max = $aXScale->GetMaxVal();
// Only draw the bar if it actually appears in the range
if ($this->min < $aXScale->GetMaxVal() && $this->max > $aXScale->GetMinVal()) {
// Trucate to limit of axis
$this->min = max($this->min, $aXScale->GetMinVal());
$this->max = min($this->max, $aXScale->GetMaxVal());
 
$y=$aYScale->scale_abs[1];
$x=$aXScale->Translate($this->min);
$height=abs($aYScale->scale_abs[1]-$aYScale->scale_abs[0]);
$width=abs($x-$aXScale->Translate($this->max));
$this->prect->SetPos(new Rectangle($x,$y,$width,$height));
$this->prect->Stroke($aImg);
}
}
}
}
 
//===================================================
// CLASS PlotLine
// Description:
// Data container class to hold properties for a static
// line that is drawn directly in the plot area.
// Usefull to add static borders inside a plot to show
// for example set-values
//===================================================
class PlotLine {
var $weight=1;
var $color="black";
var $direction=-1;
var $scaleposition;
 
//---------------
// CONSTRUCTOR
function PlotLine($aDir=HORIZONTAL,$aPos=0,$aColor="black",$aWeight=1) {
$this->direction = $aDir;
$this->color=$aColor;
$this->weight=$aWeight;
$this->scaleposition=$aPos;
}
//---------------
// PUBLIC METHODS
function SetPosition($aScalePosition) {
$this->scaleposition=$aScalePosition;
}
function SetDirection($aDir) {
$this->direction = $aDir;
}
function SetColor($aColor) {
$this->color=$aColor;
}
function SetWeight($aWeight) {
$this->weight=$aWeight;
}
function Stroke(&$aImg,&$aXScale,&$aYScale) {
$aImg->SetColor($this->color);
$aImg->SetLineWeight($this->weight);
if( $this->direction == VERTICAL ) {
$ymin_abs=$aYScale->Translate($aYScale->GetMinVal());
$ymax_abs=$aYScale->Translate($aYScale->GetMaxVal());
$xpos_abs=$aXScale->Translate($this->scaleposition);
$aImg->Line($xpos_abs, $ymin_abs, $xpos_abs, $ymax_abs);
}
elseif( $this->direction == HORIZONTAL ) {
$xmin_abs=$aXScale->Translate($aXScale->GetMinVal());
$xmax_abs=$aXScale->Translate($aXScale->GetMaxVal());
$ypos_abs=$aYScale->Translate($this->scaleposition);
$aImg->Line($xmin_abs, $ypos_abs, $xmax_abs, $ypos_abs);
}
else
JpGraphError::Raise(" Illegal direction for static line");
}
}
 
// <EOF>
?>
/trunk/api/jpgraph_1.12.2/jpgraph_log.php
New file
0,0 → 1,241
<?php
/*=======================================================================
// File: JPGRAPH_LOG.PHP
// Description: Log scale plot extension for JpGraph
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_log.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
 
DEFINE('LOGLABELS_PLAIN',0);
DEFINE('LOGLABELS_MAGNITUDE',1);
 
//===================================================
// CLASS LogScale
// Description: Logarithmic scale between world and screen
//===================================================
class LogScale extends LinearScale {
//---------------
// CONSTRUCTOR
 
// Log scale is specified using the log of min and max
function LogScale($min,$max,$type="y") {
$this->LinearScale($min,$max,$type);
$this->ticks = new LogTicks();
$this->name = 'log';
}
 
//----------------
// PUBLIC METHODS
 
// Translate between world and screen
function Translate($a) {
if( $a < 0 ) {
JpGraphError::Raise("Negative data values can not be used in a log scale.");
exit(1);
}
if( $a==0 ) $a=1;
$a=log10($a);
return ceil($this->off + ($a*1.0 - $this->scale[0]) * $this->scale_factor);
}
 
// Relative translate (don't include offset) usefull when we just want
// to know the relative position (in pixels) on the axis
function RelTranslate($a) {
if( $a==0 ) $a=1;
$a=log10($a);
return round(($a*1.0 - $this->scale[0]) * $this->scale_factor);
}
// Use bcpow() for increased precision
function GetMinVal() {
if( function_exists("bcpow") )
return round(bcpow(10,$this->scale[0],15),14);
else
return round(pow(10,$this->scale[0]));
}
function GetMaxVal() {
if( function_exists("bcpow") )
return round(bcpow(10,$this->scale[1],15),14);
else
return round(pow(10,$this->scale[1]));
}
// Logarithmic autoscaling is much simplier since we just
// set the min and max to logs of the min and max values.
// Note that for log autoscale the "maxstep" the fourth argument
// isn't used. This is just included to give the method the same
// signature as the linear counterpart.
function AutoScale(&$img,$min,$max,$dummy) {
if( $min==0 ) $min=1;
assert($max>0);
$smin = floor(log10($min));
$smax = ceil(log10($max));
$this->Update($img,$smin,$smax);
}
//---------------
// PRIVATE METHODS
} // Class
 
//===================================================
// CLASS LogTicks
// Description:
//===================================================
class LogTicks extends Ticks{
var $label_logtype=LOGLABELS_MAGNITUDE;
//---------------
// CONSTRUCTOR
function LogTicks() {
}
//---------------
// PUBLIC METHODS
function IsSpecified() {
return true;
}
 
function SetLabelLogType($aType) {
$this->label_logtype = $aType;
}
// For log scale it's meaningless to speak about a major step
// We just return -1 to make the framework happy (specifically
// StrokeLabels() )
function GetMajor() {
return -1;
}
 
function SetTextLabelStart($aStart) {
JpGraphError::Raise('Specifying tick interval for a logarithmic scale is undefined. Remove any calls to SetTextLabelStart() or SetTextTickInterval() on the logarithmic scale.');
}
 
function SetXLabelOffset($dummy) {
// For log scales we dont care about XLabel offset
}
 
// Draw ticks on image "img" using scale "scale". The axis absolute
// position in the image is specified in pos, i.e. for an x-axis
// it specifies the absolute y-coord and for Y-ticks it specified the
// absolute x-position.
function Stroke(&$img,&$scale,$pos) {
$start = $scale->GetMinVal();
$limit = $scale->GetMaxVal();
$nextMajor = 10*$start;
$step = $nextMajor / 10.0;
$img->SetLineWeight($this->weight);
if( $scale->type == "y" ) {
// member direction specified if the ticks should be on
// left or right side.
$a=$pos + $this->direction*$this->GetMinTickAbsSize();
$a2=$pos + $this->direction*$this->GetMajTickAbsSize();
$count=1;
$this->maj_ticks_pos[0]=$scale->Translate($start);
$this->maj_ticklabels_pos[0]=$scale->Translate($start);
if( $this->supress_first )
$this->maj_ticks_label[0]="";
else {
if( $this->label_formfunc != '' ) {
$f = $this->label_formfunc;
$this->maj_ticks_label[0]=$f($start);
}
elseif( $this->label_logtype == LOGLABELS_PLAIN )
$this->maj_ticks_label[0]=$start;
else
$this->maj_ticks_label[0]='10^'.round(log10($start));
}
$i=1;
for($y=$start; $y<=$limit; $y+=$step,++$count ) {
$ys=$scale->Translate($y);
$this->ticks_pos[]=$ys;
$this->ticklabels_pos[]=$ys;
if( $count % 10 == 0 ) {
if( $this->majcolor!="" ) {
$img->PushColor($this->majcolor);
$img->Line($pos,$ys,$a2,$ys);
$img->PopColor();
}
else
$img->Line($pos,$ys,$a2,$ys);
 
$this->maj_ticks_pos[$i]=$ys;
$this->maj_ticklabels_pos[$i]=$ys;
if( $this->label_formfunc != '' ) {
$f = $this->label_formfunc;
$this->maj_ticks_label[$i]=$f($nextMajor);
}
elseif( $this->label_logtype == 0 )
$this->maj_ticks_label[$i]=$nextMajor;
else
$this->maj_ticks_label[$i]='10^'.round(log10($nextMajor));
++$i;
$nextMajor *= 10;
$step *= 10;
$count=1;
}
else {
if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
$img->Line($pos,$ys,$a,$ys);
if( $this->mincolor!="" ) $img->PopCOlor();
}
}
}
else {
$a=$pos - $this->direction*$this->GetMinTickAbsSize();
$a2=$pos - $this->direction*$this->GetMajTickAbsSize();
$count=1;
$this->maj_ticks_pos[0]=$scale->Translate($start);
$this->maj_ticklabels_pos[0]=$scale->Translate($start);
if( $this->supress_first )
$this->maj_ticks_label[0]="";
else {
if( $this->label_formfunc != '' ) {
$f = $this->label_formfunc;
$this->maj_ticks_label[0]=$f($start);
}
elseif( $this->label_logtype == 0 )
$this->maj_ticks_label[0]=$start;
else
$this->maj_ticks_label[0]='10^'.round(log10($start));
}
$i=1;
for($x=$start; $x<=$limit; $x+=$step,++$count ) {
$xs=$scale->Translate($x);
$this->ticks_pos[]=$xs;
$this->ticklabels_pos[]=$xs;
if( $count % 10 == 0 ) {
$img->Line($xs,$pos,$xs,$a2);
$this->maj_ticks_pos[$i]=$xs;
$this->maj_ticklabels_pos[$i]=$xs;
 
if( $this->label_formfunc != '' ) {
$f = $this->label_formfunc;
$this->maj_ticks_label[$i]=$f($nextMajor);
}
elseif( $this->label_logtype == 0 )
$this->maj_ticks_label[$i]=$nextMajor;
else
$this->maj_ticks_label[$i]='10^'.round(log10($nextMajor));
++$i;
$nextMajor *= 10;
$step *= 10;
$count=1;
}
else
$img->Line($xs,$pos,$xs,$a);
}
}
return true;
}
} // Class
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/jpgraph_stock.php
New file
0,0 → 1,183
<?php
/*=======================================================================
// File: JPGRAPH_STOCK.PHP
// Description: Stock plot extension for JpGraph
// Created: 2003-01-27
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_stock.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2003 Johan Persson
//========================================================================
*/
 
//===================================================
// CLASS StockPlot
//===================================================
class StockPlot extends Plot {
var $iWidth=9;
var $iEndLines=1;
var $iStockColor1='white',$iStockColor2='darkred',$iStockColor3='darkred';
var $iTupleSize = 4;
//---------------
// CONSTRUCTOR
function StockPlot(&$datay,$datax=false) {
if( count($datay) % $this->iTupleSize ) {
JpGraphError::Raise('Data values for Stock charts must contain an even multiple of '.$this->iTupleSize.' data points.');
}
$this->Plot($datay,$datax);
$this->numpoints /= $this->iTupleSize;
}
//---------------
// PUBLIC METHODS
function SetColor($aColor,$aColor1='white',$aColor2='darkred',$aColor3='darkred') {
$this->color = $aColor;
$this->iStockColor1 = $aColor1;
$this->iStockColor2 = $aColor2;
$this->iStockColor3 = $aColor3;
}
 
function SetWidth($aWidth) {
// Make sure it's odd
$this->iWidth = 2*floor($aWidth/2)+1;
}
 
function HideEndLines($aHide=true) {
$this->iEndLines = !$aHide;
}
 
// Gets called before any axis are stroked
function PreStrokeAdjust(&$graph) {
if( $this->center ) {
$a=0.5; $b=0.5;
$this->numpoints++;
} else {
$a=0; $b=0;
}
$graph->xaxis->scale->ticks->SetXLabelOffset($a);
$graph->SetTextScaleOff($b);
}
// Method description
function Stroke($img,$xscale,$yscale) {
$n=$this->numpoints;
if( $this->center ) $n--;
if( isset($this->coords[1]) ) {
if( count($this->coords[1])!=$n )
JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
else
$exist_x = true;
}
else
$exist_x = false;
 
if( $exist_x )
$xs=$this->coords[1][0];
else
$xs=0;
$ts = $this->iTupleSize;
$this->csimareas = '';
for( $i=0; $i<$n; ++$i) {
 
//If value is NULL, then don't draw a bar at all
if ($this->coords[0][$i] === null) continue;
 
if( $exist_x ) $x=$this->coords[1][$i];
else $x=$i;
$xt = $xscale->Translate($x);
$neg = $this->coords[0][$i*4] > $this->coords[0][$i*$ts+1] ;
$yopen = $yscale->Translate($this->coords[0][$i*$ts]);
$yclose = $yscale->Translate($this->coords[0][$i*$ts+1]);
$ymin = $yscale->Translate($this->coords[0][$i*$ts+2]);
$ymax = $yscale->Translate($this->coords[0][$i*$ts+3]);
 
$dx = floor($this->iWidth/2);
$xl = $xt - $dx;
$xr = $xt + $dx;
 
if( $neg )
$img->SetColor($this->iStockColor3);
else
$img->SetColor($this->iStockColor1);
$img->FilledRectangle($xl,$yopen,$xr,$yclose);
$img->SetLineWeight($this->weight);
if( $neg )
$img->SetColor($this->iStockColor2);
else
$img->SetColor($this->color);
$img->Rectangle($xl,$yopen,$xr,$yclose);
 
if( $yopen < $yclose ) {
$ytop = $yopen ;
$ybottom = $yclose ;
}
else {
$ytop = $yclose ;
$ybottom = $yopen ;
}
$img->SetColor($this->color);
$img->Line($xt,$ytop,$xt,$ymax);
$img->Line($xt,$ybottom,$xt,$ymin);
 
if( $this->iEndLines ) {
$img->Line($xl,$ymax,$xr,$ymax);
$img->Line($xl,$ymin,$xr,$ymin);
}
 
// A chance for subclasses to add things to the bar
// for data point i
$this->ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg);
 
// Setup image maps
if( !empty($this->csimtargets[$i]) ) {
$this->csimareas.= '<area shape="rect" coords="'.
round($xl).','.round($ytop).','.
round($xr).','.round($ybottom).'" ';
$this->csimareas .= ' href="'.$this->csimtargets[$i].'"';
if( !empty($this->csimalts[$i]) ) {
$sval=$this->csimalts[$i];
$this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
}
$this->csimareas.= ">\n";
}
}
return true;
}
 
// A hook for subclasses to modify the plot
function ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg) {}
 
} // Class
 
//===================================================
// CLASS BoxPlot
//===================================================
class BoxPlot extends StockPlot {
var $iPColor='black',$iNColor='white';
function BoxPlot($datay,$datax=false) {
$this->iTupleSize=5;
parent::StockPlot($datay,$datax);
}
 
function SetMedianColor($aPos,$aNeg) {
$this->iPColor = $aPos;
$this->iNColor = $aNeg;
}
 
function ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg) {
if( $neg )
$img->SetColor($this->iNColor);
else
$img->SetColor($this->iPColor);
$y = $yscale->Translate($this->coords[0][$i*5+4]);
$img->Line($xl,$y,$xr,$y);
}
}
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/Changelog
New file
0,0 → 1,1700
30 May 2003 JpGraph 1.12.2
==========================
* [me & A. Tang] Added support for Chinese font and character handling.
This requires you to install the "simhei.ttf" Chinese font.
 
* [J Sweat] Fix bug [#124] Make step line style ignore NULL values in data
 
* [J Sweat] Fix bug [#123] Divide by zero for line length of zero for dashed lines
 
* [me] Fix bug [#109]. Problem with pie color when a supplied color array
contains more colors than data. For anti-aliased images that meant that
the last n colors was used and not the first n colors.
 
* [me] For plotmarks that use the ALT string the wrong value was in due
to the recent addition of both Y and X value to control the plot callback.
This has now been fixed so that it will work as before and that previous
code that assumes one parameter in their ALT srting will see the Y-value at
the plotmar. New is also that the X-value is given as a second
parameter. This means that the ALT string can display both the Y and X value
for a specific point.
* [me] Make it possible to disable individual plot legends through the method
Plot::HideLegend()
 
* [me] Fixed problem with SetTextTickInterval() together with grouped bars
(bug #116)
 
* [me] Fixed a problem with shadows on accumulated bar plots (bug #111)
 
* [me] Fixed Legend::SetMarkAbsSize()
 
* [me] Add error check for the case that the server (Windows)
doesn't support SystemRoot and TEMP server variables.
 
* [me] Fix short-open-tags bug that made it's way into 1.12.1 (bug #121)
 
* [me] Make sure we only pass integers into GD when positioning texts.
 
* [me] Always treat "" (empty string) as NULL for bar plots
 
* [me] Allow links from vertical position 0 on gantt charts
 
 
12 Apr 2003 JpGraph 1.12.1
==========================
 
* [me] Added Legend::SetLineSpacing() to control the vertical line
distcance (in pixels) between legend texts.
 
* [me] Compensate for bug in imagecopyresampled() in GD 2.01.
This made clipping unusable with GD 2.01
 
* [me] Added LinePlot::SetFillFromYMin() to make the fill start from
the bottom of a Y-scale with negative values rather than the 0-value
which is now the case.
 
* [me] Fix a problem with legend box layout in the case of TTF fonts
and different lengths of individual texts. The layout engine now
work out each column individually. This will also give the legend
box the minimum possible size.
 
* [me] Compensate for possible bug in strtok() when parsing color
specification as a string with adjustment factors.
 
 
06 Apr 2003 JpGraph 1.12
========================
* [me] Added possibility to specify the position of the legend box
with absolute pixels coordinates not only as fractinos as before.
 
* [me] Improved legend layout. It is now possible to specify
the number of columns in the legend box. The preferred way of
specifyin the layout is now Legend::SetColumns(). The oild
way with SetLayout() is still available for backwards compatibility
reasons. The old vertical layout corresponds to a 1 column
layout (SetColumns(1) which is also the default).
 
* [me] Added possibility to specify text string position
using scale coordinates onto the plot by adding
Text::SetScalePos(). The autoscaling has been modifyied to
be aware of the text scale positions.
 
* [me] Added builtin images for plotmarks. This also meant a
refactoring exercise for the PlotMark class and it has now been
split in sevaral files.
 
* [me] Added footer capability to Radar, Pie and Gantt charts
 
* [me] Improved upon rounding problem with GD2 and pie slices
(defect #88)
 
* [me] Take shadow color as specified with Graph::SetShadow() into account.
 
* [me] Don't rotate the footer if the graph is rotated.
 
* [me] Added Axis::HideLabels()
 
* [me] Adjusted of the automatic margin calculation so that it is
aware of when the x-axis have been hidden.
 
* [me] Added PlotMark::SetCallbackYX(), This allows for the
specification of a callback function which is given both X and Y
coordinates. Also extended the returned values from the callback so
that image in a marker can be dynamically modified both size and
actual image file used. This will enable the use of advanced
"push-pin" graphs. For example adding markers to a background image
map of certain size and type.
 
For backwards compatibility any callback specified with
PlotMark::SetCallback() will not be given the extra parameter. So
old code does not need to be changed.
 
* [me] Fixed handling of PHP array URL parameters with CSIM. [Bug #82]
 
* [me] Added Legend::SetFrameWeight() which adjusts the frame around
the legend. Legend::SetLineWeight() now works as documented and sets
the line weight for the markers instead.
 
* [me] Autoscaling will now also take the value of added lines into account.
 
* [me] Fixed problem with horizontal legend box becomming too small
for many legends.
 
* [me] Radar plot. SetColor() always sets a fill color. Fixed (Bug #83)
 
* [me] Added SetWidth() for tab-title. The tab can now easily extend
over the entire plot width. Added SetPos() as synonym in tab title
for SetTabAlign().
 
* [me] Gradient bar plots now show a gradient mark in the legend
 
* [me] PiePlot3D::SetSliceColors() now more correctly specifies the
color anti-clockwise. The same order that slices are drawn.
 
* [me] Pie position can now also be specified in absolute pixels
in PiePlot::SetCenter()
 
* [me] Added Bevel frame for entire graph
 
* [me] Added possibility for some more advanced Graph title
formatting through the Graph::SetTitleBackground() method.
 
* [me] Type safe comparison in auto-detection of GD version
 
* [me] Added Graph::SetCanvasColor() to specify the background color
the canvas is initialized with.
 
* [me] Fine tuned label positiong for pie labels and made 3D pies
take the specified labels into account.
 
* [me] Added Anti-aliasing to Pie charts. Looks good but it really
slows down the pie generation. Enable by acalling
PieGraph::SetAntiAliasing()
 
* [me] Ignore NULL values in Stock and bar plots
 
* [me] Some minor internal adjustments for rounding in log scales.
 
 
2 Feb 2003 JpGraph 1.11
=======================
* [Martijn] Compensate for GD 2.x bug in filledarc() which doesn't
like negative angles.
 
* [me] Added Graph::SetBackgroundGradient() . This allows
a gradient to be used as a background. The gradient can either fill
the whole frame, only the margin or just the plot area. See the
class reference or the manual for details regarding this method.
 
* [me] Modified adjustment factor for colors so that it now
is linear between dark and light, i.e. a factor of 1 is the original
color, a factor of 0 is black and a factor of 2 is white.
 
* [me] Added two new gradient style GRAD_LEFT_REFLECTION and
GRAD_RIGHT_REFLECTION
 
* [me] Some fine tuning of text layout in legend box
 
* [me] Radar plots can now have a background image and multiple
titles (sub-title, and subsub title).
Added RadarGraph::SetPos() as alias for
RadarGraph::SetCenter().
 
* [me] Fixed a small pre-read-loop error which could casue the
X-gridline to be outside the right edge of the plotarea if the
X-scale was indented.
 
* [me] Added possibility to use (scaled) images as markers by
introductin the new mark type MARK_IMG.
 
* [me] Added tab titles for graphs. See class reference and Example
tabtitleex1.php.
 
* [me] Added filled grids by adding method Grid::SetFill(). This new
feature is illustrated in example filledgridex1.php. Documented in
section 6.2.11 in the manual.
 
* [me] Improved automatic margins so they adapt to one or more title
lines automatically.
 
* [me] Fixed a class hierachy bug. RotImage::DashedLine() should not be
implemented but rather rely on the Image::DashedLine() since the
rotation will happen in the RotImage::Line() which gets called in
Image::DashedLine() by any istance of RotImage().
You could see the ffect of this by trying to create a 90 degrees
rotated graph with dashed gridlines, they would appear not to be
rotated at all where in fact they were rotated twice.
 
* [me] Added automatic default margins when using Set90AndMargin()
 
* [me] Added Box plots which is a variant of Stock charts with the
possibility to add the median as a horizontal line in the box.
 
* [me] Added Stock charts, (aka candle-bars) as new class StockPlot().
added example stockplotex1.php in the Example directory.
 
* [me] Added two new axis styles AXSTYLE_YBOXOUT, AXSTYLE_BOXIN which
suplicates the Y-axis on both sides but not the X-axis.
 
* [me] Set90AndMargin() now also adjusts the label alignment on the
axis to suit the rotation.
 
* [me] Added class LineErrorPlot which alolows a line plot to
have error bars (+/- delta error).
 
* [me] Added Ticks::SetSize()
 
* [me] Modified size for plot mark circle so it will have the
specified width as radius and not as diameter.
 
* [me] Added Ticks::SetSize() to be able to set the absolute size
in pixels of the tick marks.
 
* [me] Default directories for Fonts and Cache now gets set
automatically.
 
 
13-Jan 2003 JpGraph 1.10.1
==========================
 
* [me] Fix bug in Band pattern Left diagonal and Right Diagonal where
the drawing was incorrect when width < height
 
* [me] Take the border on/off switch into account for line AddArea()
 
* [me] Added color parameter to Axis::SetTickLabels() to make it
possible to set individual colors on labels.
 
* [me] Fixed PiePlot::SetSliceColor() so that colors are now specified
in the same order as slices.
 
* [me] Gantt, Radar and Canvas graph now also considers the special file
_IMG_HANDLER (returns the actual image handler and doesn't stream
picture back)
 
* [Christian Reiser] Pie graphs can now also return the IMG_HANDLER
 
* [Richard Black] Removed case sensitivity for extension of
background images. Added support for handling file names with
multiple '.' characters in the path for background images.
 
* [me] Added error check for imagettfbbox() since it can give
a file not found error even though both file_exists() and
is_readable() reports that the file exists and is readable
in the case when PHP basedir restriction is in place.
 
* [me] Corrected CSIM handling in Gantt for non-file based
Gantt charts.
 
* [me] Note to self: 1.10p
 
* [me] Gracefully handle the abnormal case where no valid data points
is included in the Y-data array when creating a line plot.
 
16-Dec 2002 JpGraph 1.10
========================
* [me] Added GanttGraph::CreateSimple() whihc is a utility method to
help create simple gantt charts with a minimum of code.
 
* [me] Added new plot (FieldPlot) type which handles field plots. This is
basically a variant of scatter plots where each point is represented
as an arrow which a specific direction (0-360 degrees).
 
* [me] Added Graph::SetY2OrderBack() to control of plots on Y2 should
be under (in z-order) te plots on Y. By default they are on top.
 
* [me] Avoided a potential 1-pixel off rounding problem if a user
supplies a non integer canvas width and height in the Graph() call.
 
* [me] Added two new formats for week scale in Gantt
charts. WEEKSTYLE_FIRSTDAYWNBR, WEEKSTYLE_FIRSTDAY2WNBR These
formats adds the weeknumber as well to displaying the date of the
start of the week. Also tweaked the auto-sizing of the Gantt charts
to adjust for the size of the text in the week scale.
 
* [me] Fixed wrong define in jpgraph_gantt.php for WEEKSTYLE_DAY2
(Bug #45)
 
* [me] Improvements on manual scaling. If a manual scale is specifed
(in the Graph::SetScale()) you are now longer required to specify a
major and minior tick interval (but of course you can still do it
with a call to Ticks::Set() ).
A suitable intervall will be automatically
caclulated in the same way as for autoscaling. By default the exact
limits for the scale as specified will be used. This means that
depending on the scale and tick intervall chosen the max value of
the scale may or may not fall on a major tick mark. If you want that
behaviour you need to call Scale::SetAutoTicks(). By using this call
you give the algorithm permission to slightly adjust the specified
min and max values (if needed) so that they wil fall on a even
major step.
 
* [me] Added error check if user tries to use SetTextLabelStart()
with a logarithmic scale (where it is undefined).
 
* [me] Default marker for scatter plots are now a filled square
 
* [me] Adjusted the x-axis labelling for a 1-pixel off limit. Which
sometimes (depending on the image size and scale) could cause a
linear scale's last label not to be shown.
 
* [me] Fixed bug report #40
 
* [me] Fixed positioning of caption for full odometers. (Bug #34)
 
* [me] Fixed spelling mistake in jpgraph_odo.php which casued
Odometer::SetLabelPos() to have no affect. (Bug #33)
 
* [me] Fixed an overzeoulous optimization which skipped both rotation
and translation for angle=0. Addresses (Bug #36)
 
* [me] Colors can now include an alpha channel specification as well.
The alpha channel is specified in the text string separated by a
a '@' , for example "red@0.5". The alphachannel is specified as a
fraction between 0.0 and 1.0.
 
* [me] Improved filled line plots so they now handle negative values
in a more standard way.
 
* [me] Added automatic detection of what version of GD is installed.
 
* [me] Aligned the order of legends and slices for pie and 3D pies so
that they now follow the more common order of clockwise slices with
legends from top to bottom.
 
* [me] Changed call to deprecated function SetLinePos() to SetLineSide()
 
* [me] Added clipping to generated plots. This means that if you have
plots (for example lines) which extends outside the scale range it
will be clipped. This might come in handy in for example balloon
plots. Clipping is enabled by default but can be controlled by a
call to Graph::SetClipping()
* [me] Fixed a problem with circles and filled circles when they were
rotated twice in a rotated graph. (Bugtraq #26)
 
* [me] Added Graph::Set90AndMargin() which helps for doing a 90
degrees rotation and getting the margins right. (Otherwise you have
to adjust them manually since height becomes width and width becomes
height when you rotate 90 degrees)
Changed the horizbar* examples to use the Set90AndMargin()
 
* [me] Changed the behaviour of plot marks in legends. The possible
format callback is no longer called. Instead the plotmarks are
set to a default size.
 
* [me] Added three more plotmarks MARK_LEFTTRIANGLE,
MARK_RIGHTTRIANGLE, MARK_FLASH. These new types are primarily meant
to be used with GanttCharts.
 
* [me] Added constrain lines to GanttCharts. It is now possible to
illustrate start-end, start-start, end-start and end-end
constrains. This is illustrated by adding lines with arrows,
Use GanttPlotObject::SetConstrain()
 
* [me] Added Image map support for Gantt chart. Bar and titles can now
have associated image map targets. Use GanttBar::SetCSIMTarget()
GanttBar::SetCSIMAlt() and for titles use
GanttBar::title::SetCSIMTarget() and GanttBar::title::SetCSIMAlt()
 
* [me] Fix problem in Gantt graph whereby the progress bar indicator
could extend over the end date if an explicit date scale was set.
 
* [me] By default logarithmic scale now print the labels as 10^3
instead of 1000. You can change the behaviour with the new
method for logarithmic ticks, SetLabelLogType().
Also added the possibility to have a callback for log scales
 
* NOTE TO SELF 1.9p
 
* [me] Added the possibility to specify a value > 1 as the position
for labels in PieGraph::SetLabel() to increase the margin between
the label and the edge of the pie.
* [Olaf Kandel] Fixed subtle bug in the printing of the month scale for the
case where the start and end month hjave the same number but
are on different year. This would cause the width for the
first month to be wrong.
 
 
Jpgraph 1.9.1
=============
* [me] Fixed defects #20 and #21 (spelling mistake and and one
overzelous last minute error check I added). I have to eat a lot
of humble pie over these ones. Sorry to all who have been affected.
What can I say? Human error........
 
JpGraph 1.9
===========
* [me] Changed the way TTF fonts are handled. The font files are no
longer distributed with JpGraph due to potential copyright problem.
This change also has the advantage that it is now very
straigthforward to install new fonts whatever their naming
conventions are.
 
1. If you are running on a standard Windows system just point the
TTF directory to your standard font directory and use the available
fonts in windows.
 
2. If you are on a Unix system you can download the MS Core TTF
files from http://corefonts.sourceforge.net/. The old MS EULA allows
distribution of the fonts in unaltered form (Win EXE) and this
project exercises this right and provide tools to download and
install the fonts on a UNIX system.
 
* [me] Renamed this release from 1.8.1 to 1.9 due to the big
change in the handling of TTF fonts which breaks backward
compatibility due to the copyright problem with TTF fonts.
 
* [me] Improved the TTF pargagraph layout engine for consistency
in handling "\n\r" vs "\n". It is now always sufficient to
use just "\n".
 
* [me] Adjusted the text positioning for StrokeBoxedText()
to use the new GetBBoxHeigh() and GetBBoxWidth().
 
* [me] Added Image::GetBBoxHeight(), Image::getBBoxWidth()
 
* [me] Once and for all fixed the handling of rotated TTF text by
adding Image::GetBBoxTTF() which returns the bounding rectangle
(which is not the same as the bounding box except for angle==0)
 
* [me] Added argument to Legend::SetColor() to make it possible
to specify both frame and font color.
 
* [me] Cosmetic adjustment of markers in legend so that they are
positioned exactly in the middle of the text string.
 
* [me] Corrected the positioning of TTF fonts when defining the
hotspot as 'bottom'. It could be up to 4 pixel off when using
texts that contained character that extended below the TTF
baseline e.g. '('.
 
* [me] Added possibility to use a cache system with CSIM images
as well. The cache system is used slightly, slightly different
and require a call to the new method Graph::CheckCSIMCache()
This is unfortunately unavoidable due to the nature of CSIM in
HTML. The documentation is updated to deal with this. This also
added two more defines CSIMCACHE_DIR and CSIMCACHE_HTTP_DIR
 
* [me] Added CSIM (Client Side Image Maps) to legends, added
target and alt arguments to all Plot::SetLegend()
 
* [me] Added functionlity to pass on any URL arguments when using
StrokeCSIM() (Bug: #006)
 
* [me] Fixed so that Gantt charts can again have just the width
specified and have the height automtically sized. (This was a
regression introduced in 1.8) (Bug #005)
 
* [me] Added error check for return value from imaagecolorsforindex()
in Anti-aliased lines. In combination with GD2, no-true-color, a
true color background (ala JPG) this can fail since it gets a color
which doesn't exist (since it uses imagecolorat()).
 
* [me] Improved edges support in 3D Pies.
 
* [me] Changed Image::Polygon() so i now draws an open polygon and not
a closed one.
 
* [me] Legends for scatter plots no displays the mark used for
each scatter plot and not just a square with the correct
color.
 
* [me] Fixed a small cosmetic problem in that horizontal legend
box was sometimes not wide enough and made a small adjustment
of the positioning of marks in the legend box for better
apperance. (Bug #0017)
 
* [me] Improved manual by adding linked TOC.
 
* [me] Added Bezier curve to canvas Shape class. Added Bezier canvas
example canvasbezierex1.php
 
* [me] Added Paragraph Alignment setting in Text::Align() as well to
avoid seprate call to Text::SetParagraphAlign()
 
* [me] Added Image::SetExpired() . By default the image now sends
back a couple of variants of "no-cache" headers to force the browser
to reload.
 
* [me] Some serious speed improvments for 3D Pie plots. It can now be
up to 6000% faster (yes that's right 6000%!). This is the way I had
planned on doing it but for time reasons were pushed out of 1.8.
I have completely changed the algorithm that handles the individual
slices on the expense of a slightly more complicated implementation.
This means that there is no performance difference between 2D and
3D Pie Plots.
 
* [me] Fix bug for rotation of paragraphs (multi-line) texts.
A limitation for now is that rotated pargraphs always gets
paragraphs align "left". (Bug: #008)
 
* NOTE TO SELF: 1.8p
 
* [me] Fixed a typo that had the strange affect that paragraph strings
which started with a number would have larger distance between the
individual lines since that number would be interpretated as the
angle for the text!
 
* [me] Fixed a problem with marks getting out of sync if NULL values
are present in the line plot. (Bug #0018)
 
* [me] Scatterplot no skips NULL values instead of treating them
as zero.
 
* [me] Added LinearScale::SetAutoMax() as complement to SetAutoMin()
 
* [me] Added LinearScale::SetAutoTicks() this makes it possible to
manually set a scale but still have the tick marks automatically
determined. By default this feature is off to keep old code
behave exactly the same as before. If this is enabled any
Ticks::Set() calls are ignored.
 
* [me] Added possibility to adjust the position of the displayed
value for bars via the BarPlot::SetValuePos() method. Added new
example example20.5 to demonstrate this.
 
* [me] Bugfix: Fixed color themes for 3D pie plot. The color array
was not wrapped around in the case where more slices (data points)
than colors is used. (Bug: #001)
 
* [me] Documentation is now 100% pure HTML and does not require
any PHP. Previously a PHP script was used to generate the
highlighted source used in the image/source frames. All this
is now pre-generated in the docs.
 
* [me] Position the values in a scatter plot in the center of the
plot mark by default (Setting align to 'center','center' and margin=0
 
* [me] Added LinePlot::SetBarCenter to make it possible to center the
line on the bars when combining a plot with both bars and lines.
The new example 'ceneterdlinebarex1.php' demonstrates this.
 
 
 
[19-Sep-2002] JpGraph 1.8
=========================
* [me] Added Text::SetPos() as alias for Text::Pos() to harmonize with
other setting methods.
 
* [me] Made Graph::Add() polymorphic. You can now use Graph::Add() to
add all plot objects and you don't longer have to use AddText(),
AddLine(), AddBand() and so on.
 
* [me] Added PiePlot::SetLabelPos(), added some more Pie examples.
 
* [me] Improved the API for class Text by adding SetShadow() to make
it more like the other objects. Extended Text::SetBox() with shadow
width and corner radius.
 
* [me] Added the possibility for Text object to use the new rounded
rectangles as the filled background.
 
* [me] Added possibility to add arbitrary labels inside the pie plot
 
* [me] Added special filename __IMG_HANDLER to make it possible to
have Graph::Stroke() return the image handler instead of stroking
the image to file (or stream it back).
 
* [me] Improve FilledCircle() to avoid moire-patterns in circle.
 
* [me] Added middle type pie plot
 
* [me] User specified values for the band position is truncated to the
min and max values of the axis.
 
* [me] Added possibility to use Text::SetMargin() for all the titles
in a graph
 
* [me] Added SetSize() as synonym for SetWidth() for plot marks.
 
* [me] Updated some of the examples with more comments.
 
* [me] Fixed problem with with jpg background images wrongly being
loaded with imagecreatefromjpg instad of imagecreatefromjpeg
 
* [me] Corrected the positioning of individual titles for 3D
plots.
 
* [me] Added Text::SetMargin() to make it easy to have context
sensitive margins.
 
* [me] Added capability to have background images on Pie and 3D
Pie plots
 
* [me] Added method Axis::HideLine() which hides the axis line but
nothing else.
 
* [me] Fixed bug in Stroke::Text() where negative coordinates
wasn't properly handled. We must allow negative absolute
coordinates since these might be rotated/translated into
"real" screen coordinates. This fixes the bug, for example
in bar_csimex1.php where the first value of the bar wasn't
displayed.
 
* [me] More adjustment of how the rounding takes place with
coordinates. This should now avoid all potential difference
between different graphic objects.
 
* [me] When you use automatic labelling on text scale it now starts
at 1 and not at 0. This makes more sense since most people (except
mathematicians) start counting from 1.
 
* [me] Example directory:
- Added more advanced canvas examples
- Updated all CSIM examples
- ~60 new example script which are used in the new documentation
 
* [me] Compensate for bug in imagettfbox() which return to small size for
large fonts.
 
* [me] Added jpgraph_canvtools.php which contain some utility classes for
use with canvas graphs.
 
* [me] DOCUMENTATION: The class reference is now included in the release.
 
* [me] DOCUMENTATION: Completely rewritten the original
Word-introduction to JpGraph to pure HTML. This will be a living
document that will be updated as time moves on. This is currently
considered "Beta" and more chapters need to be added but this is
a good start!!
 
* [me] Added full support for multiline legends. (See for example
nullvalueex01.php. Also improved the way lineplot with marks
are illustrated in the legend by adding the line underneth the
plotmark.
 
* [me] Added some utility classes to be able to draw DB schema for
DDDA. This is tailored for my DB abstraction layer (jpdb.php)
in DDDA but is generic enough to work in other circumstances as well.
The classes can be found in utils/misc/imgdbschema.inc
Please see (run) dbschema_ddda.php which shows how it looks
in a real life example.
 
* [me] Tweaked the positioning of TTF fonts and how the width and
height are determined. Making more carefull assumptions about
how the baseline is handled for individual lines of text.
 
* [me] Added class RectangleText() which makes it easier to work
with text inside a rectangle with rounded corner.
 
* [me] Added support for multi-line TTF paragraphs with paragraph
formatting. This was previously only supported for the builtin
fonts. All text object should now support fully formatted paragraphs
of text.
 
* [me] Added Image::RoundedRectangle() and
Image::FilledRoundedRectangle()
 
* [me] Added format callback to plot marks. This makes it
possible to individually adjust the plot marks depending
on it's specific data value. See balloonex1.php
 
* [me] Added some initial support for super script used for
scientific numbering, i.e properly print for example 10^2
where the 2 is in superscript. This is all handled by the
new class SuperScriptText(). Not yet used in the actual
labels drawing but will be added shortly.
 
* [me] Added a small tool mkgrad.php in the utils directory to
make it a breeze to create gradient squares which may be
used as background images.
 
* [me] Added footer class to graph. This makes it easy to have
left,center and right footers in your graphs.
 
* [me] Added subsubtitle to graphs
 
* [me] Improved/Changed the way you work with CSIM. This new method
makes it possible to generate CSIM without having to create an
intermediate image on disk.
Added Graph::StrokeCSIM(). Added new chapter in the docs on how to
work with CSIM
 
* [me] Changed naming of Spider* to Radar* since SpiderGraph is a
copyrighted name.
 
* [me] Modified the automatic suppression of the first value of the
X-axis when the Y-axis have negative value so it don't happen
in the case where the X-axis is positioned at the minimum of
the Y-axis.
 
* [me] Fixed spelling error of BK* instead of the correct BG*
for the default settings of background image style.
 
* [me] Some internal code changes to RGB() class to make it easier
to use directly for external scripts.
 
* [me] Changed the behaviour of filled line plot so that they
don't go down to 0 for null values in the plot but instead
behaves like '-' for non-filed plots, just ignore them and
connect the previous and next point instead.
 
* [me] Fixed problem in integer scaling where it certain scale
combinations could generate floating point labels! Ooops..
(Affects IntCalcTicks())
 
 
[5-July-2002] JpGraph 1.7
==========================
* [me] Removed the APACHE_CACHE_DIRECTORY define since it is
not used.
 
* [me] Added Image map functionlity for rotated bar graphs,
also added new example for this (bar_csimex3.php)
 
* [me] Made Ticks::SetPrecision() deprecated. Use SetFormatString()
instead
 
* [me] Fixed problem with incorrect string being returned from
ToCyrillic()
 
* [me] Allow Y2 axis to use "int" scale
 
* [me] Fixed problem with wrongly names framework function for
patterns
 
* [me] Fixed small problems with margins being slightly off for
roated images.
 
* [me] Changed name of PlotMark::SetMarkColor() to PlotMark::SetColor()
for consistency
 
* [me] Added example with "hidden" Y-axis.
 
[17-June-2002] JpGraph 1.7-BETA
===============================
* [me] Added DisplayValue::SetFormatCallback() to make it possible to use
a user specified formatting callback routine.
(Note:: Yes, this is very nono-OO but the proper OO way, by forcing the
user to define a subclass of the plot class where the formatting method
is overridden will probably not fly with to many non-OO PHP peoples since
a callback is conceptually simpler.)
 
* [me] Added DisplayValue::HideZero(). Makes it possible to hide zero values
in labels, for example zero slices in pie's, (e.g. $pieplot->value->HideZero())
 
* [me] Added manual scaling to Gantt graphs, added example to illustrate
this, fixscale_gantt.php
 
* [me] Adding image maps for Y2 scale as well.
 
* [me] Fixed a small bug which made it impossible to use BRAND_TIMING with
Gantt charts. Enabling timestamping of graphs would give a "PlotArea to small"
error.
 
* [me] Made a very small change to the AutoScaling algorithm to yield tighter
boundaries, i.e with the existing algorithm, depending on rounding problems
a plot which had a maximum value of, say 4, could get a autoscaling max
value of 5. Not a big problem (or hardly a bug) but not what I wanted.
All my 111 test cases works fine with the modification but I'm still
not convinced that this (very minor) adjustment is 100% (I can distinctly
remember that I had a good reason to do it the original way, I just can't
remember what :-)
 
* [me] Graph titles may now have multiple rows. By default the lines will
be centered, but this may of course be adjusted by a call to, for
example, $graph->subtitle->ParagraphAlign('left');
 
* [me] Added class FuncGenerator() to make it easy to generate
function plots (both normal and polar). To illustrate this I have
added three new examples funcex1.php,funcex2.php,funcex3.php.
 
* [me] Added Axis::HideFirstLastLabel() to hide first and last label on
axis. This is usefull if we have a box around the plot area and we don't
want the labels to owerwrite the box (see funcex1.php)
 
* [me] Make sure GD2 variables is registred in $GLOABALS[]
 
* [me] Fixed bug in image maps for marks. Alt strings was incorrectly
indexed so it was only using values 0,2,4.. instead of 0,1,2,...
 
* [me] Added Graph::SetAxisStyle(). This makes it easy to use
a few number of preset axis setup. For example duplicating the axis
as a border around the plot. Preset axis position availabel are
AXSTYLE_SIMPLE, AXSTYLE_BOXIN, AXSTYLE_BOXOUT
 
* [me] Made Axis::SetTickLabelMargin() deprecated. Use
Axis::SetLabelMargin() instead. Name change to make it more
consistent with the other margin settings.
 
* [me] Made Ticks::SetSide() a synonym for Ticks::SetDirection()
to make it consistent with side setting for labels and values.
 
* [me] Added Axis::HideZeroLabel() as a shortcut for
axis->scale->ticks->SupressZeroLabel()
 
* [me] Internal code changed. Replace the use of FontProp in SpiderGraph
with Text() and some additoinal code cleanup.
 
* [me] Adjusted Image::Arc() so that no negative angles are
passed on to GD imagearc() since it barfs at negative angles.
 
* [me] Added PiePlot3D::SetHeight() which is used to specify the
height in pixels of the 3D pies instead of using the default
height.
* [me] Pie plots (both 2D and 3D) now displays the slice labels
by default.
 
* [me] Added two more Examples. barintex1.php shows how to use
integer scale and barscalecallbackex1.php shows how to use
a callback function for formatting the scale.
 
* [me] Modified grace calculation for Integer scale to have higher
sensitivity.
 
* [me] Restructured code so class DateLocale is now in jpgraph.php
rather than in jpgraph_gantt.php. Also added $gDateLocale as a
global instance of the class DateLocale
 
* [me] Modified integer autoscale to correctly handle very small values
(needs special handling of max==1 to avoid fraction)
 
* [me] Changed the way min value is extracted when finding the baseline
for bar plots. Now uses the function Scale::GetMinVal() instead of
reading the value directly fomr the instance variable. This makes it
work better with logscales so that the bar starts from the min value
of the log scale. Also added check in bar PreStrokeAdjust() to see if
we are using a log Y-scale to automatically adjust the Y-base.
 
* [me] Optimized Stroke() loop for LinePlot so calls to PlotMark::Stroke()
will only be done if the marks are really to be shown.
 
* [me] Added Axis::SetLabelAlign().
 
* [me] Made some internal modifiaction on how Axis title gets aligned
to make it possible to specify it manually without having it
automtically set. This makes it possible to generate nice
horizontal bar graphs.
* [me] Added three examples in the Example directory on how to make
horizontal bar graphs.
* [me] Don't always set Y-axis position automtically
 
* [me] Add Text::SetAngle() as synonym for SetOrientation()
Added SetLabelSide() as synonym for SetlabelPos()
 
* [me ]Make it possible to manually specify the alignment of the textstring
for the title position. This makes title looks nicer with horizontal
bar graphs.
 
* [me] Allow Y-axis labels to be at an angle
 
[14-May-2002] JpGraph 1.6.3
===========================
Changes/Additions:
* [me] Added functionality to make AccBarPlot::SetAbsWidth() work in
jpgraph_bar.phps
 
* [me] LinePlot::SetColor() no longer sets the color of the plotmark.
The problem that some people had was that if you set the mark color
specifically first and then unsuspecting called SetColor() for the
line the color for the plotmark circumference would also be set making
it look like the PlotMark::SetColor() didn't work.
 
* [me] Improved the functionality of GanntGraph::SetDateRange() to make
it possible to display a section of the graph.
(In previous version it was a checked error if you tried to plot
a graph where any activity bar was outside the specified area.
This is now changed to only cap the bars to the area.
This makes it more suitable to handle big gantt charts
which now can be split into several images to show different
parts of the projects)
 
* [me] Added GanttScale::SetWeekStart() to set the start of week to an
arbitrary day. For example SetWeekStart(0) will make the weeks start
on Sunday, SetWeekStart(1), Monday and so on.
 
* [me] Fixed a rounding error in Gantt chart that could cause the week
start day to off by one.
 
* [me] Modified the Min() and Max() methods for AccLineBar's to avoid
them overestimating the max value. (Basically now using the same
methods as in AccBarPlot)
 
* [me] Added missing code to do percentage calculation for 3D Pies so
they work the same way as the 2D Pies.
 
* [me] Added error check for the case where the image is so small that
the available plot area becomes to small.
 
* [me] Compensate for a PHP bug (#12474) in strtotime() which can affect
Gantt chart so that the same month name is shown twice.
See http://bugs.php.net/bug.php?id=12474
* [me] Compensate for the documented shortcomming in my
3D Pie algortitm whereby a small exploded slice close to 270 degrees
get's slighthly nagged at tha base due to the fact that the z-order
stroking algorithm is only perfect if the slices are not exploded.
 
 
[20-April-2002] JpGraph 1.6.2
=============================
Changes/Additions:
* [me] Slightly adjusted LinearTicks::Stroke() to better handle som
potential rounding problems.
 
* [Thomas George] Added LinePlot::AddArea() which makes it possible
to just fill part of a line plot (a part that is specified with a
start and end x-point)
 
* [Thomas George] Fixed bug with missing coordinate when using filled
line plot with step style.
 
* [me] Added client side image maps to scatter and line plots
 
* [me] Fixed problem with warning "IMG_ARC_CIRCLE not defined" when
running on GD 1.x
* [me] Removed test with is_null() in jpgraph.php to make JpGraph 1.6.2
work with older versions of PHP (especially 4.02).
 
[07-April-2002] JpGraph 1.6.1
=============================
Changes/Additions:
* [me] Modified autoscaling for the case where all values are equal to
make the min/max difference larger.
 
* [me] Fixed bug in 3D pies for certain data values
 
* [me] Fixed bug with images maps assuming that slices
where order anti-clockwise and the new pie algorithm did them
clockwise.
 
* [me] Added subtitle to Pie and Pie3D graphs.
 
[02-April-2002] JpGraph 1.6
===========================
Changes/Additions:
* [me] Changed license to QPL
 
* [me] Completely rewritten algorithm for 3D pies which gives MUCH better result. Will
also allow exploded 3D pies.
 
* [D. Reinbach] Better handling of filling line plot with negative
values
 
* [me] Added LinePlot::StrokeDataValue() for easy handling of plotting
data point values.
* [Gyozo Papp,me] Real locales for Gantt chart. The locales now take a
proper locale string (e.g. "en_UK", or "sv_SE") to get the real
locales from the system. Note that this breaks the sc compatibility
 
* [me] Removed jpgraph_dir.php and moved the directory settings back in
jpgraph.php due to problems in PHP with nested includes and search
paths.
 
* [me] Added possibility to show values for scatter plot by
refactoring value support back into jpgraph.php Plot() class. This
makes it easy for all subclasses that uses Plot() to add display
value support.
 
* [me] Added possibility to use X-coordinates in Error plots
 
* [me] Fixed missing LogScale::SetXLabelOffset(). This made it
impossible to use linear plots with a log x-scale !!!
 
* [me] Fixed bug so possible Y2 plots are also taken into account when auto
scaling the X-axis. This could otherwise couse plots outside the
scale when using Y and Y2 plots where Y2 plots had more data values.
* [me] **********!!SC BREAK!!**************
Aligned the way values are displayed on Bar graphs with line and
scatter plots. The values to be displayed for these plots are now
all accessed through the "value" property of the plot. For example
$myBarPlot->value->Show();
instead of the previous
$myBarPlot->ShowValue();
 
* [me] **********!!SC BREAK!!**************
Aligned the way values are displayed in Pie plots with line and
bar plots. You know use the "value" property in exactly the same way
as in bars and lines.
 
* [me] **********!!SC BREAK!!**************
Aligned the way values are displayed in 3D Pie plots with line and
bar plots. You know use the "value" property in exactly the same way
as in bars and lines.
 
* [me] Added possibility to display individual values in an
accumulated bar graph in addition to the overall accumulated
value. This is done as usual through setting the individual bars
value property to what you want (size, color, format and so on)
 
* [me] Added another default error handler which is image based to
make it easier to see error messages when images are included
in a page. This is now the default error handler.
 
* [me] Added possibility to specify the location of the X-Axis
to the top of the graph by using xaxis->SetPos("max") also
added new Example topxaxisex1.php to demonstrate this feature.
 
* [me] Added more 3D pie examples to demonstrate new functionlity.
 
[01-March-2002] JpGraph 1.5.3
=============================
Changes/Additions:
* [me] Make it possible to use JpGraph 1.5.3 with GD 1.8.x. In 1.5.2 I was
using a function only available in GD 2.x.
 
[29-Feb-2002] JpGraph 1.5.2
===========================
 
Bug fixes:
----------
* [me] Released the correct 3D pie file (By mistake I mixed the stable version with
the development branch for 2.0 in the 1.5.1 release just to prove
that I'm still human :-)
 
* [me] Fixed out of bounds error when using shadows for bar graphs
with 0 value
 
* [me] Fixed typo in DisplayValue::SetFormat()
 
* [me] Fixed potential array out of bounds error in Plot::Min() and
Plot::Max()
 
* [me] Fixed misplaced ')' in string construction in call to
JpGraph error.
 
Changes/Additions:
------------------
* [me] Added possibility to use "auto" file name when stroking
directly to a file.
 
 
* [me] Added tag 'title' to the image maps to make for better
compatibility with Mozilla.
 
* [me] Moved class LineProprty to jpgraph.php from jpgraph_gantt.php
since it can be used in other modules as well.
 
* [me]Added Text::GetTextHeight() which differs from
Text::GetFontHeight() in that it takes the total text height
(possible multiple lines) into account.
 
* [me] Added Image::FilledArc()
 
* [me] Added RotImage::SetTranslation()
Added RotImage::Circle()
Added RotImage::FilledCircle()
Added RotImage::Arc()
Added RotImage::FilledArc()
 
 
[11-Feb-2002] JpGraph 1.5.1
===========================
* [me] No longer treat a not set $y[0] value as an explicit error. Instead
silently set $y[0] to 0.
 
* [me] Changed "include" to "require" in jpgraph.php when including
jpgraph_dir.php. This seems to fix the strange problem whereby
the constants defined in jpgraph_dir.php didn't have a correct
value in jpgraph.php.
 
* [me] Fixed problem with correctly drawing of bars when all bars
are negative. (Previously the baseline wasn't correctly handled
since min/max values needs to be mirrored for negative bars)
Note: Funny this problem has gone unnoticed so long.
 
* [me] Fixed problem with doing gradient fill for negative bars.
 
* [me] Fixed problem with autoscaling when there is a very small
difference (< 0.00001) between min and max and when both min and max are
negative. This might happen if you have a single negative value.
 
* [me] Added labels for line graph. Each point my now also optionally
display it's value on a line graph. Value are controlled trough
class DisplayValue and it's instance in LinePlot.
LinePlot->value->Show(),
LinePlot->value->SetColor(),
LinePlot->value->SetFont(),
LinePlot->value->SetMargin(),
LinePlot->value->SetFormat(),
[27-Jan-2002] JpGraph 1.5
=========================
* [me] Fixed misnamed xmin variable in Min() for acc bars
 
* [me] Added possibility to specify paragraph alignment for
multi line text, i.e the text can be left,right or center aligned
within it's bounded box. The lignment is set by the new method
Text::ParagraphAlign()
 
* [me] Changed position specification for text so that if the position
is specified in range [0,1] it is interpreted as fraction of image
height width and otherwise as absolute pixels.
 
* [me] Added possibility to specify pie size (radius) both as a fraction and
absolute size in pixels.
 
* [me] Added possibility to tweak color specification by adding a
saturation percentage, i.e you can specify color as "red:0.65"
which means 65% of the red value (in other words 35% darker).
 
* [me] Tweaked the estimation of number of segments to approximate an
arc in Image::CakeSlice.
 
[17-Jan-2002] JpGraph 1.5beta2a
===============================
* [me] Fixed typo in Max() calculation for accumulated bar graphs
which caused it to return wrong value.
 
* [me] For Min() calculation for acc bars graphs take the user
specified ymin into account.
 
* [Peter Svistunov, me] Added Cyrillic unicode support through the
global define LANGUAGE_CYRILLIC. This is also the start of a
generic support for Unicode languages through the LanguageConv
class.
 
 
[15-Jan-2002] JpGraph 1.5beta2
==============================
* [me] Rewrite of the pie-chart drawing algorithm. This version
is slightly slower but avoids using GD floodfill function. This
fixes problems with small slices not being filled completely. As
an added bonus it is now possible to explode (and specify the
explode radius) for all slices. It is also possible to draw the
pie without having internal borders between the slices as well
as turning on/off the border surronding the pie.
 
* [me] Added graphic primitive CakeSlice() to the Image class.
Used to simplify pie-drawing.
 
* [me] Added more generic error handling. It is now possible to
install your own error object with
JpGraphError::Install(<name of error object>)
The default error handler will just abort execution.
 
* [me] Security whole in utility script for displaying source
and image in frames fixed.
 
* [Cedric Scheyder] Actively ignore hours in calculating Max/Min
values for dates in Gantt charts to avoid problems in autoscaling
when users specify start and end dates down to hours.
 
* [Rick Widmer] Fixed problem in calculating min/max value when first
Y-value was non-numeric.
* [me] Fixed calculation of width and height of text with multiple lines.
Multiple text lines (text blocks) should now be almost fully supported.
 
 
[16-Dec-2001] JpGraph 1.5-BETA
==============================
* Take "\n" into (some) account when calculating string height and width
Text output now supports multiple lines better.
 
* [me] Removed reference to global PHP_SELF, use HTTP_SERVER_VARS array
instead. This makes JpGraph working with 'register_globals' turned
off.
 
* [me] Added some rudimentary check that GD is enabled before trying to
run JpGraph.
 
* [me] Fixed silly cut-and-paste bug in AccBarPlot::Max() which could
cause to small max values to be returned
 
* [me] Added property 'title' to PlotMark. The title is printed in the center
of the plotmark.
 
* [DanNY (dulcis28@hotmail.com)] Added JpGraph Logo. (Thanks!)
 
* [me] Added jplintphp.php, jpgendoc.php, jplintdriver.php which are
parsing, documentation and pretty printing tools I use to help
me to documentation and find unused code.
Tthat does some simple static analysis on PHP for silly mistakes
that I do (Like using instance variables without qualifying them
with "$this->" ) The tool also warns about unused instance variables.
Since it also parses the classes and methods I use it to generate
the documentation skeleton for the reference section.
 
* [me] Added Gantt chart funcionality, jpgraph_gantt.php . See
Gantt tutorial for help on usage.
 
* [me] Added new Gantt tutorial
 
* [me] Added BKIMG_CENTER, possibility to center the background image.
 
* [me] Timeout function now also aplies when you generate an image
off-line, i.e if the imagefile exist and is within the time window
now file will be written.
* [Rasmus Lerdorf] Added patch to make background images work with GD 2.x
N.B GD 2.01 has bugs in handling TTF font with truecolor images which
are fixed in 2.02 but that has (of this writing) still not been officially
released. GD 2.x support in JpGraph is still to be considered experimental.
 
* [me] Fixed some potential rounding errors in 2D Pie plot which in some
circumstances might have caused a flood fill to escape. Also added check
to better handle very small slices so we don't try to do a fill
on the border edge.
 
* [me] Slightly changed the internal design for band patterns.
They are now created by a factory class instead. This makes it much easier
to re-use and have maximum code reuse.
 
* [me] Factored out some functionlity from Graph::Stroke() to make it possible
to re-use some code in GanttGraph::Stroke()
 
* [me] One more instance when PHP surprised me by thinking 0 == "-" aarrghhh
Now I think (knock on wood) all problems with data points=0 should be solved
 
* [me] Added '&' to the assignment in Graph::Add() This now lets you specify changes
for lines, plots even after they been added to a Graph. But I'm not sure if this
is really such a good ide because you will have problem if you write code
like
for( .... ) {
$myplot = new LinePlot(.....);
$graph->Add($myplot);
}
Since this will not work the way you think (why?) Because the same
reference to the variable $myplot will be used for all additions and
hence the graph will only have multiple instances of the last added
LinePlot(). Have to think of this and what is the best way. (I will
probably never get used to PHP references....)
 
 
[11-Nov-2001] JpGraph 1.4
==========================
Bug Fixes:
-------------------------------------------------------------------------------------
* [me] Enabled E_NOTICE warnings in my PHP installation and found a couple of
glitches (harmeless out-of-bound in two loops) which is now corrected.
* [me] Fixed problem in line plots where y-value==0 wasn't drawn. (Once again
bitten by the fact that in PHP the following is true: ""==0)
* [me] Fixed color specification for bars when initialized as an array of colors
* [me] Fixed color of bars using shadow which was advertedly set to shadow color
* [me] Fixing incorrect color specification for legends using bars with color arrays
* [me] Add check for sum=0 in pie plots to avoid divide by 0 problem
* [me] Changed the stroke orderd for FilledRectangle() so that the shadow doesn't
overwrite a line with weight > 1
* [me] Fixed incorrect calculation of font height for titles which cause the
subtitle to be to close to the title.
 
Additions:
-------------------------------------------------------------------------------------
* [me] Added depth parameter (DEPTH_FRONT, DEPTH_BACK) for gridlines
Graph::SetGridDepth()
 
* [me] Added possibility to add static horizontal and vertical band with a
pattern in the plot area. Graph::AddBand() Supports solid, diagonal,
stright line, 3D-plane crosses (both diagonal/vertical) patterns.
Graph::AddBand()
* [me] Added possibility to have static lines in the plot. This is now implemented
with new class PlotLine and two new functions Graph::AddLine(),
* [me] Added symbolic constants to specify background image types
BGIMG_COPY, BGIMG_FILLPLOT, BGIMG_FILLFRAME
 
* [me] Added extended label formatting through either
Axis::SetLabelFormatString() C-style formatting string
or
Axis::SetLabelFormatCallback() A callback function that are given the value to be displayed
and should manipulate the value as it likes
The use of the old style digit precision variable will be deprecated from next version.
 
* [me] Added error check if supplied Y-data has unspecified value at index 0 to avoid
out of bounds error when corrupted data is sent in to JpGraph.
* [me] Added possibility to specify image depth for gridlines. You can now specify
Graph::SetGridDepth() as DEPTH_FRONT or DEPTH_BACK. Depending on if you want
the gridlines at the back of in front of your plots.
 
* [me] Added possibility to specify Cache file name as "auto". In this case the
image file name will have the same name as the scriupt that generates the
image with the extension of the automtically generated graphic format.
NOTE: You will get the wrong extension using this feature at the same
time as specifying a different graphic format thne the automatically choosen
one.
 
* [me] Added some more advanced examples for impuls drawings impulsex3.php and
impulsex4.php
 
* [me] The values on top of bars can now be at an angle and the horisontal
position now account for if a shadow is present.
* [me] Added filename to Graph::Stroke() which makes it easy to output an image
directly to a file _without_ streaming it back to the browser. Usefull for
batch processing
* [me] Added new tutorial for bar graphs
 
* [me] New scale type "int". This gives a normal linear scale but restricts
ticks (and labels) to integer values. Can be used for both X and Y scales.
 
* [me] Added subtitle to graph
 
* [me] Added possibility to specify an array of X-coordinates with bar plots to
position individual bars on the X-scale. Makes it much easier to combine
bar plots with other types of plots, (line, scatter etc)
* [me] Extended utility adjimage.php to allow it to manipulate color saturation.
 
* [me] Added Image::AdjSat() to allow manipulation of the color
saturation in the image. This also makes it very easy to change the image
to greyscale (just set saturation=-1).
Extended Graph::AdjImage() and Graph::AdjBackgroundImage()
to include new parameter for saturation value.
* [me] Added BarPlot::SetAlign() which makes it possible to align
bars relative the tickmark for linear and int scale
* [me] Added possibility to absolutely set the width of bars. This will
override the automatically decided with based on the scale and tick distance
Changes:
-------------------------------------------------------------------------------------
* [me] Legend color for scatter plots now becomes the fill color of the mark
 
* [me] Impulsplots now anchors at Y-value = 0 if the y-axis contains both negative
and positive values. Otherwise it will go to the lowest value on the Y-axis.
 
* [me] Bar shadows is now connected to bars to make for a stronger 3D effect.
 
* [me] Removed all deprecated references and usage of FONT0, FONT1, FONT2. Note
You can no longer use these. Instead use FF_FONT0, FF_FONT1, FF_FONT2 etc
* [me] Text scale now starts default labelling at 0
 
* [me] All plots internally adjusted to start at default X-coordinate 0 to harmonize better
with X-scale.
 
* [me] Graph->Stroke() now just returns instead of exit()
 
* [me] Removed flag argument for BarPlot::SetShadow()
 
* [me] Bar plots are know by default filled with lightblue
unless otherwise specified.
* [me] Changed the way bars are positioned for linear and integer
scale for maximum flexibility when mixing barplot with other
plots.
* [me] The ALT strings for all image maps now uses printf() syntax to display
the actual value. If you have used this feature you must update your
ALT strings, for example "val=%v" must become "val=%d" or whatever format
you choose.
* [me] Changed the formatting of bar graphs by changing the framework
so that labels on the x-axis using a text scale is now independent
on the tick marks.
This makes it possible to have the labels centered under each bar but have
the tickmarks between the bars as most other commercial graph packages
format these typw of graphs. Introduced Axis::SetTextLabelsInterval()
Effects jpgraph.php, jpgraph_log.php and jpgraph_bar.php
* [me] Renamed Axis::SetTextTicks() to Axis::SetTextTicksInterval() to make the name
more descriptive. The old name is deprecated from this version on but will still
work until 2.0 to keep compatibility.
* [me] Cleaned up implementation of pie3d so it now inherits from pie to avoid
a lot of duplicated code and easier maintainance. (A good example of the real
life OO programming where you might not see the similarities between two classes
until you fully implement them.) File size of jpgraph_pie3d.php has shrunk by
roughly 40%
[23-Sep-2001] JpGraph 1.3.1
===========================
Additions:
* [Michael Anthon] Added client side image maps to 3D pie plots
* [me] Added possibility to better control the labeling of pie graphs with an optional
label formatting string PiePlot::SetLabelFormat(), PiePlot::SetLabelType()
This also makes it possible to have labels with the absolute value and not only the
percentage. (same with pie3d)
* [me] Added formatting capabilities for legends on pie and pie3d graphs. Can have the value
automtically included (Se Examples/pieex6.php)
* [me] Added some more examples, negbarvalueex1.php, scatterlinkex2.php, pie3d_csimex1.php
pieex6.php
 
Known bugs and omissions:
- Client side image maps does not currently handle exploded pie graphs (just
normal pie graphs.
 
Bug fixes:
* [me] Fixed problem with positioning the displayed values for negative bars
* [me] Fixed shadow for negative bars
* [me] Fixed problem in Plot::Min(), PlotMax::Max() whereby zeros where not counted
towards minimum since in PHP 0=="". This could sometimes cause strange autoscaling.
* [Vitaly E. Ashmarin] Wrong arguments to imagejpeg() for combination of specified
image quality and filename = ""
* [John Milne] BarPlot::SetFillColor() fixed glitch in loop variable
 
 
[13-Sep-2001] JpGraph 1.3
=========================
Note: I have to the best of my memory tried to give credit where
credit is due. Howevever, since ver 1.2.2 I have received over 2000 mail about
JpGraph with Questions/Suggestions/Improvments (and a few bug fixes) etc and
I might have missed some in the process of going through these mails. The
fault is completely my own and if you recognize some idea I have implemented
something which you think you should have credit for please drop me a note and
I will adjust the add you to the change notes.
 
Known bugs and omissions:
- The automatic value on bar graphs does not work well with
negative bar graphs. (Quite easy to fix though..)
- Client side image maps does not currently handle exploded pie graphs (just
normal pie graphs.
 
IMPORTANT NOTICE:
* SC (SOURCE COMPATIBILITY) BREAK !: If you want to rotate the image the parameter
is no longer supplied directly in the call to Graph() but rather as a method
call Graph::SetAngle(). I decided to make this SC break since I judged it more
natural to supply the timeout value just efter the specified cache-file name
in the Graph() call. Hopefully this will not hit to many people since my hunch is
that few people uses the rotate function and it's very easy to fix those script
that does so.
 
Bug Fixes:
* [michael@anthon.net] Corrected behviour of accumulated bar graph so it now
diffrentiate between the accumulated positive and negative values.
* [marko@fly.srk.fer.hr] Fixed Max() calculations for accumulated bar graphs
where my original code was a little bit sloppy. (I simply took the max for
each plot and added those and this might be more then the real maximum.)
* [m.purgar@extracom.de] Spotting an incorrect "jpg" in the image where it should
have been "jpeg"
* [adam.blomberg@euroseek.net,patrik.johansson@euroseek.net] 3D-Pie filling
problem for small slices due to the discrapency between GD arc() and the
JpGraphs purely mathematical definition of an ellipse.
* [delorme.maxime@free.fr] 2D-Pie Floodfill might "escape" for very small values of slices.
 
* [et.al] Improved handling of 0:s vs "" values. This was necessary since PHP treats
0 as "" the same in a number of situations and JpGraph needs to diffrentiate these
two cases. This caused 0 data values to be treated as null values and not be inluded
in (for example) autoscaling consideration.
 
Additions:
* [me] Added new global constant USE_CACHE which makes it possible to disable
writing to the cache even if a filename is supplied in th Graph::Graph() method.
 
* [me] Added LinePlot::SetStyle() to make it possible to make dashed, dotted etc
lineplots. (See example1.2). Also adjusted legend to display the same style in
the legend.
* [michael@anthon.net, me] Added client side image maps for all types of bars
and 2D pies with possibility to have the actual value shown in an ALT-tag.
* [me] Added drop shadow (or rather "right-up" shadow) to bar graphs BarPlot::SetShadow()
 
* [me] Added possibility to have the actual value of bar graphs displayed at top of graphs
via BarPlot::ShowValue(), BarPlot::SetValueFont(), SetValueMargin(), SetValueColor()
The format of the value is specified according to standard C printf() string formatting
i.e. "val=%d" will for example print a string "val=13"
* [me] Added possibility to adjust brightness and contrast for background image
via the Graph::AdjBackgroundImage() method.
* [me] Added possibility to adjust brightness and contrast in the finished image
via the Image::AdjBrightContrast() method.
* [me] Added possibility to just generate an image to a file and not stream
it directly back to the browser. Added parameter $aInline to Graph::Graph()
* [me] Added timout for cache, i.e if the image in the cache is older then
the specified number of minutes (=0 never re-generate, -1 always regenerate)
then re-generate the image. Just specify a timeout in minutes with a call:
$mygraph->cache->SetTimeOut(10)
* [me] Added possibility to use logarithmic scale for SpiderPlots. This also resulted
in a little bit of internal cleanup in jpgraph_spider.php.
* [me] Added SetWeight() method to class Ticks() for control of line thickness for ticks
 
* [me] Added SetMarkColor method to class TIcks() which lets you specify different
colors for major and minor tick marks.
* [me] Added method RelTranslate() to LinearScale() and LogScale() classes which is used
in Spider class. This makes a world to screen relative translation.
* [me] Added possibility to link data points in a scatter plot with lines
by the addition of method ScatterPlot::SetLinkPoints()
* [me,john.milne@one2one.co.uk] Added possibility to have individual colors of
bars in bar graphs by having BarPlot::SetColor() accept an array as argument.
* [sergio@alsernet.es] Make it possibly to have x-tick labels to consist of two lines
by inserting a "\n" separator in the text.
* [CK1@wwwtech.de] Adding JPEG quality setting. This is done by adding
a method SetQuality() to the img class. After creating a graph this can then
be used as $mygraph->img->SetQuality($some_qvalue).
Currently this only affects JPEG images.
* [delorme.maxime@free.fr] Possibility to use different grace value for top and bottom
of graph and not just a single value as in 1.2.2
 
Changes:
* [me] Changed group/permission handling for created files to better adjust itself
to the way the local Apache user is setup
* [me] The default for spider graphs is now NOT to be filled
 
* [me] Allow all datapoints to be 0 which will set the scale to [0,1]
 
* [me] Removed the need for a DIR_BASE variable
 
[29-Apr-2001] JpGraph 1.2.2
===========================
Bug fixes:
* Removed reference to non-existent property 'bypass' in class RotImg
* Changed to allow the last X-gridline to be drawn (comparison with limit was '<'
where it should have been '<=' )
 
Additions:
* Added possibility to use background image
* Added possibility to use approximate color through the "USE_APPROX_COLORS"
constant
* Handling of diffrent form of "null values" for line graphs. An y-value
can now be "" or "-". In the first case (a true null value) this mark
will cause a disruption in the line graph to indicate that the value
is undefined. In the other case specifying the value as "-" will cause
the value to be ignored bu the line will still be drawn between the
previous data point and the following. Neither of these cases will have
any mark drawn.
* Added a fatal warning when no more colors can be allocated for the chosen
graphic format.
 
Changes:
* "Downgraded" anti-aliasing algorithm to use fewer color levels
in the transition to avoid using up to much of the color palette
* Cleaned up some of the code to not rely on PHP default initialization
of variables. This will help those who have got warnings when they
have had all error reporting enabled in php.ini (E_ALL)
* Legends now show the plot mark (if defined) instead of just a square of
the right color.
 
Acknowledgements:
* Thanks to kevin@pricetrak.com for suggesting the treatment of
pure NULL values.
* Thanks to luca_n@hotmail.com for getting my attention to the warnings
caused by relying on PHP default initialization of variables.
(My installation had E_NOTICE disabled, hence I never got those warning myself.)
 
 
[29-Mar-2001] JpGraph 1.2.1
===========================
Bug fixes:
* When min and max values for autoscaling was equal this resulted in trying to calculate
log10(0) = INF resulting in an infinite loop in LinearTicks::Stroke()
* When autoscaling was used with only one data point it tried to calculate log10(0) = INF
resulting in an infinite loop
* fopen("xxx","r") should be fopen("xxx","rb") causes problems on windows system with IIS5
* JPG streaming function was incorrectly called "imagejpg" should be "imagejpeg" (see the difference?)
* Non existent color name in jpgraph_pie.php
* Removed all deprecated forced references in function calls
* The width of the surrunding box was not calculated correctly for internal bold font
 
Additions:
* Color themes for pie graphs
* Beta release of 3D pie graphs
* Added DIR_BASE as constant to make it possible to use one system wide copy
of JpGraph. NOTE: This must be set to the directory where JpGraph is installed.
 
Acknowledgements:
* (ck1@wwwtech.de) (and a russian guy which I unfortunately lost the name of)
for reporting the imagejpg spelling mistake
* (ales.gregor@zps.skoda-auto.cz) for finding the binary file problem with IIS
* Several people have reported the problem with autoscaling when all Y-values
were equal. Thanks to you all for acting as my privet QA team!
 
 
[18-Mar-2001] JpGraph 1.2
=========================
Additions:
----------
* Added "BRAND_TIMING" which give possibility to brand each image generated with
the time (in ms) it took to generate the image.
* Added Gradient Fill for bar graphs, The gradient fill allows 6 different styles.
* Added Anti-aliasing for lines. Note drawing anti-aliased lines is 7-10 times
slower then "normal" lines!
* Added full TTF support. this means an internal SC break (Source Code Break). Normal JpGraph
script should not be affected if you have used only publized "public" frunctions. The SC break is
to allow uniform treatment of both internal fonts and TTF. This has been achieved by changing
the parameter list for SetFont(). However, backward compatibility with the old naming conventions
for internal fonts are kepot, e.g. SetFont(FONT1_BOLD) still works but is deprecated and will not
be valid from 2.0
* Added possibility to control font for title of Spider Axis through the added
property 'title' to the axis in the spider graph.
* Added 'SetColor()' in pie graphs as a shortcut to SetMarginColor() to set the background color
* Added possibility to draw X-axis labels at arbitrary angle. Internal fonts only supports
horizontal and verical.
* Added possibility to draw boxed text at an angle
* Added possibility to have different colors for axis and the labels on the axis by the addition
of extra parameter to SetColor()
* Added 350 more named colors.
* Added canvas "fake" graph to make it easy to draw arbitrary graphics
* Added SetCenter() method for line graphs.
* Added SetGrace() for autoscaling purposes.
 
Changes:
--------
* If you use a text-scale for X-axis the default labels now start at 1 instead of 0 since this is
actually a counting scale and it makes more sense to start at 1.
* Improved handling of bar-graphs with negative values.
* Updated existing examples to use new format for SetFont()
* Made it a critical error to specify a non existant font
* Changed so that orientation for Text() is now given as an angle, i.e. SetOrientation(45). Old
style of using SetOrientation("v") (for vertical) is deprecated. But will work until V2
 
Bug fixes:
----------
* Improved handling of computational effects on small values in scaling where a rounding error might cause the
last label not to be drawn.
* The position for the title of X-axis could in some cases be slightly different in first
and second pass due to incorrectly determination of the X-axis labels font height.
* Specifying an minimum Y-value for Y-axis on bar graphs could on rare occasions (combination
of scale values and specified density of autoscaling ticks) cause a gap between the bottom of
each bar and the X-axis.
 
 
[18-Feb-2001] JpGraph 1.1
=========================
Additions/Changes:
------------------
* Added Spider graphs
* Added Pie graph
* Added Scatter (and impuls) graphs
* Added possibility to rotate plots an arbitrary angle
* Added step style rendering to lineplot
* Added Breseham circle drawing which gives better visual apperance
then the built in Arc() in GD (on the expense of CPU and performance)
* Improved Polygon and Rectangle drawing so that it will use line weight settings
* Improved the visual appearance of lines with weight>1 by correctly calculating
(angle wise) the endpoints and using a filled polygon to draw the line.
Unfortunately it would require some real improvements in the GD library to really improve
the visual appeance of lines with weight>1. This is as good as it gets without writing
a low level pixe-by-pixel correct plotting. With my method you get it to look visually
aestethic right in 90% of the cases with just 10% work.
* Added MARK_FILLEDCIRCLE as new mark type. Note that due too the poor performance of the
basic Arc() image primitive in PHP4 the circles isn't perfect circles, they tend to be a
a little bit flat at multiples on PI/2.
* Added the possibility to have a separate fill- and line- color for marks
* Moved Class PlotMark from jpgraph_line.php to base in jpgraph.php since this class is also used
with scatter plots.
* Made it a critical error to try to use Text X-scale with specified X-data points.
Previously no warning was given for this non-defined state. (Using text scale with specified
points really doesn't make sense.)
* Made it a critical error to use unknown color rather than silently replacing it with black.
* Updated documentation to reflect added capability
 
 
Bug fixes:
----------
* Fixed minor bug in Line() in which it didn't update the last point for use with
subsequent LineTo() calls (forgotten $this->)
* Eliminated assumptions in DashedLine() that x1<=x2 and y1<=y2 eliminates the
potential pixel overshoot.
* Fixed a serious bug in Line plot when used with a specified X-scale (not the normal default)
since the X-coordinate wasn't read from the correct vector!
* Fixed a serious bug whe using as specified X-scale since the maximum value for a plot
wasn't correctly passed to the autoscaling.
Note: The reason that these two bugs haven't been discovered previously is the fact
that my test specs didn't include test cases to do with specified X-axis (I almost never ever use that).
This has now been corrected and added to the test suit.
* Fixed so the possible box around the plot area correctly honours the weight specified for it.
 
 
[05-Feb-2001] JpGraph 1.0
=========================
* Initial release.
 
[EOF]
/trunk/api/jpgraph_1.12.2/jpgraph_line.php
New file
0,0 → 1,426
<?php
/*=======================================================================
// File: JPGRAPH_LINE.PHP
// Description: Line plot extension for JpGraph
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_line.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
// constants for the (filled) area
DEFINE("LP_AREA_FILLED", true);
DEFINE("LP_AREA_NOT_FILLED", false);
DEFINE("LP_AREA_BORDER",false);
DEFINE("LP_AREA_NO_BORDER",true);
 
//===================================================
// CLASS LinePlot
// Description:
//===================================================
class LinePlot extends Plot{
var $filled=false;
var $fill_color='blue';
var $mark=null;
var $step_style=false, $center=false;
var $line_style=1; // Default to solid
var $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
var $barcenter=false; // When we mix line and bar. Should we center the line in the bar.
var $fillFromMin = false ;
 
//---------------
// CONSTRUCTOR
function LinePlot(&$datay,$datax=false) {
$this->Plot($datay,$datax);
$this->mark = new PlotMark();
}
//---------------
// PUBLIC METHODS
 
// Set style, filled or open
function SetFilled($aFlag=true) {
JpGraphError::Raise('LinePlot::SetFilled() is deprecated. Use SetFillColor()');
}
function SetBarCenter($aFlag=true) {
$this->barcenter=$aFlag;
}
 
function SetStyle($aStyle) {
$this->line_style=$aStyle;
}
function SetStepStyle($aFlag=true) {
$this->step_style = $aFlag;
}
function SetColor($aColor) {
parent::SetColor($aColor);
}
function SetFillFromYMin($f = true ) {
$this->fillFromMin = $f ;
}
function SetFillColor($aColor,$aFilled=true) {
$this->fill_color=$aColor;
$this->filled=$aFilled;
}
function Legend(&$graph) {
if( $this->legend!="" ) {
if( $this->filled ) {
$graph->legend->Add($this->legend,
$this->fill_color,$this->mark,0,
$this->legendcsimtarget,$this->legendcsimalt);
} else {
$graph->legend->Add($this->legend,
$this->color,$this->mark,$this->line_style,
$this->legendcsimtarget,$this->legendcsimalt);
}
}
}
 
function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
if($aMin > $aMax) {
// swap
$tmp = $aMin;
$aMin = $aMax;
$aMax = $tmp;
}
$this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
}
// Gets called before any axis are stroked
function PreStrokeAdjust(&$graph) {
 
// If another plot type have already adjusted the
// offset we don't touch it.
// (We check for empty in case the scale is a log scale
// and hence doesn't contain any xlabel_offset)
if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
$graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
if( $this->center ) {
++$this->numpoints;
$a=0.5; $b=0.5;
} else {
$a=0; $b=0;
}
$graph->xaxis->scale->ticks->SetXLabelOffset($a);
$graph->SetTextScaleOff($b);
//$graph->xaxis->scale->ticks->SupressMinorTickMarks();
}
}
function Stroke(&$img,&$xscale,&$yscale) {
$numpoints=count($this->coords[0]);
if( isset($this->coords[1]) ) {
if( count($this->coords[1])!=$numpoints )
JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
else
$exist_x = true;
}
else
$exist_x = false;
 
if( $this->barcenter )
$textadj = 0.5-$xscale->text_scale_off;
else
$textadj = 0;
 
// Find the first numeric data point
$startpoint=0;
while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) )
++$startpoint;
 
// Bail out if no data points
if( $startpoint == $numpoints )
return;
 
if( $exist_x )
$xs=$this->coords[1][$startpoint];
else
$xs= $textadj+$startpoint;
 
$img->SetStartPoint($xscale->Translate($xs),
$yscale->Translate($this->coords[0][$startpoint]));
 
if( $this->filled ) {
$cord[] = $xscale->Translate($xs);
$min = $yscale->GetMinVal();
if( $min > 0 || $this->fillFromMin )
$cord[] = $yscale->Translate($min);
else
$cord[] = $yscale->Translate(0);
}
$xt = $xscale->Translate($xs);
$yt = $yscale->Translate($this->coords[0][$startpoint]);
$cord[] = $xt;
$cord[] = $yt;
$yt_old = $yt;
 
$this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);
 
$img->SetColor($this->color);
$img->SetLineWeight($this->weight);
$img->SetLineStyle($this->line_style);
for( $pnts=$startpoint+1; $pnts<$numpoints; ++$pnts) {
if( $exist_x ) $x=$this->coords[1][$pnts];
else $x=$pnts+$textadj;
$xt = $xscale->Translate($x);
$yt = $yscale->Translate($this->coords[0][$pnts]);
$y=$this->coords[0][$pnts];
if( $this->step_style && is_numeric($y) ) {
$img->StyleLineTo($xt,$yt_old);
$img->StyleLineTo($xt,$yt);
 
$cord[] = $xt;
$cord[] = $yt_old;
$cord[] = $xt;
$cord[] = $yt;
 
}
else {
if( is_numeric($y) || (is_string($y) && $y != "-") ) {
$tmp1=$this->coords[0][$pnts];
$tmp2=$this->coords[0][$pnts-1];
if( is_numeric($tmp1) && (is_numeric($tmp2) || $tmp2=="-" ) ) {
$img->StyleLineTo($xt,$yt);
}
else {
$img->SetStartPoint($xt,$yt);
}
 
if( is_numeric($tmp1) &&
(is_numeric($tmp2) || $tmp2=="-" || ($this->filled && $tmp2=='') ) ) {
$cord[] = $xt;
$cord[] = $yt;
}
}
}
$yt_old = $yt;
 
$this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
 
}
 
if( $this->filled ) {
$cord[] = $xt;
if( $min > 0 || $this->fillFromMin )
$cord[] = $yscale->Translate($min);
else
$cord[] = $yscale->Translate(0);
$img->SetColor($this->fill_color);
$img->FilledPolygon($cord);
$img->SetColor($this->color);
$img->Polygon($cord);
}
 
if(!empty($this->filledAreas)) {
 
$minY = $yscale->Translate($yscale->GetMinVal());
$factor = ($this->step_style ? 4 : 2);
 
for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
// go through all filled area elements ordered by insertion
// fill polygon array
$areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
$areaCoords[] = $minY;
 
$areaCoords =
array_merge($areaCoords,
array_slice($cord,
$this->filledAreas[$i][0] * $factor,
($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1)) * $factor));
$areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
$areaCoords[] = $minY; // last y
if($this->filledAreas[$i][3]) {
$img->SetColor($this->filledAreas[$i][2]);
$img->FilledPolygon($areaCoords);
$img->SetColor($this->color);
}
// Check if we should draw the frame.
// If not we still re-draw the line since it might have been
// partially overwritten by the filled area and it doesn't look
// very good.
// TODO: The behaviour is undefined if the line does not have
// any line at the position of the area.
if( $this->filledAreas[$i][4] )
$img->Polygon($areaCoords);
else
$img->Polygon($cord);
 
$areaCoords = array();
}
}
 
if( $this->mark->type == -1 || $this->mark->show == false )
return;
 
for( $pnts=0; $pnts<$numpoints; ++$pnts) {
 
if( $exist_x ) $x=$this->coords[1][$pnts];
else $x=$pnts+$textadj;
$xt = $xscale->Translate($x);
$yt = $yscale->Translate($this->coords[0][$pnts]);
 
if( is_numeric($this->coords[0][$pnts]) ) {
if( !empty($this->csimtargets[$pnts]) ) {
$this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
$this->mark->SetCSIMAlt($this->csimalts[$pnts]);
}
if( $exist_x )
$x=$this->coords[1][$pnts];
else
$x=$pnts;
$this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
$this->mark->Stroke($img,$xt,$yt);
$this->csimareas .= $this->mark->GetCSIMAreas();
$this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
}
}
 
 
}
} // Class
 
 
//===================================================
// CLASS AccLinePlot
// Description:
//===================================================
class AccLinePlot extends Plot {
var $plots=null,$nbrplots=0,$numpoints=0;
//---------------
// CONSTRUCTOR
function AccLinePlot($plots) {
$this->plots = $plots;
$this->nbrplots = count($plots);
$this->numpoints = $plots[0]->numpoints;
}
 
//---------------
// PUBLIC METHODS
function Legend(&$graph) {
foreach( $this->plots as $p )
$p->DoLegend($graph);
}
function Max() {
list($xmax) = $this->plots[0]->Max();
$nmax=0;
for($i=0; $i<count($this->plots); ++$i) {
$n = count($this->plots[$i]->coords[0]);
$nmax = max($nmax,$n);
list($x) = $this->plots[$i]->Max();
$xmax = Max($xmax,$x);
}
for( $i = 0; $i < $nmax; $i++ ) {
// Get y-value for line $i by adding the
// individual bars from all the plots added.
// It would be wrong to just add the
// individual plots max y-value since that
// would in most cases give to large y-value.
$y=$this->plots[0]->coords[0][$i];
for( $j = 1; $j < $this->nbrplots; $j++ ) {
$y += $this->plots[ $j ]->coords[0][$i];
}
$ymax[$i] = $y;
}
$ymax = max($ymax);
return array($xmax,$ymax);
}
 
function Min() {
$nmax=0;
list($xmin,$ysetmin) = $this->plots[0]->Min();
for($i=0; $i<count($this->plots); ++$i) {
$n = count($this->plots[$i]->coords[0]);
$nmax = max($nmax,$n);
list($x,$y) = $this->plots[$i]->Min();
$xmin = Min($xmin,$x);
$ysetmin = Min($y,$ysetmin);
}
for( $i = 0; $i < $nmax; $i++ ) {
// Get y-value for line $i by adding the
// individual bars from all the plots added.
// It would be wrong to just add the
// individual plots min y-value since that
// would in most cases give to small y-value.
$y=$this->plots[0]->coords[0][$i];
for( $j = 1; $j < $this->nbrplots; $j++ ) {
$y += $this->plots[ $j ]->coords[0][$i];
}
$ymin[$i] = $y;
}
$ymin = Min($ysetmin,Min($ymin));
return array($xmin,$ymin);
}
 
// Gets called before any axis are stroked
function PreStrokeAdjust(&$graph) {
 
// If another plot type have already adjusted the
// offset we don't touch it.
// (We check for empty in case the scale is a log scale
// and hence doesn't contain any xlabel_offset)
if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
$graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
if( $this->center ) {
++$this->numpoints;
$a=0.5; $b=0.5;
} else {
$a=0; $b=0;
}
$graph->xaxis->scale->ticks->SetXLabelOffset($a);
$graph->SetTextScaleOff($b);
$graph->xaxis->scale->ticks->SupressMinorTickMarks();
}
}
 
// To avoid duplicate of line drawing code here we just
// change the y-values for each plot and then restore it
// after we have made the stroke. We must do this copy since
// it wouldn't be possible to create an acc line plot
// with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
// since this method would have a side effect.
function Stroke(&$img,&$xscale,&$yscale) {
$img->SetLineWeight($this->weight);
$this->numpoints = count($this->plots[0]->coords[0]);
// Allocate array
$coords[$this->nbrplots][$this->numpoints]=0;
for($i=0; $i<$this->numpoints; $i++) {
$coords[0][$i]=$this->plots[0]->coords[0][$i];
$accy=$coords[0][$i];
for($j=1; $j<$this->nbrplots; ++$j ) {
$coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy;
$accy = $coords[$j][$i];
}
}
for($j=$this->nbrplots-1; $j>=0; --$j) {
$p=$this->plots[$j];
for( $i=0; $i<$this->numpoints; ++$i) {
$tmp[$i]=$p->coords[0][$i];
$p->coords[0][$i]=$coords[$j][$i];
}
$p->Stroke($img,$xscale,$yscale);
for( $i=0; $i<$this->numpoints; ++$i)
$p->coords[0][$i]=$tmp[$i];
$p->coords[0][]=$tmp;
}
}
} // Class
 
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/jpgraph_error.php
New file
0,0 → 1,154
<?php
/*=======================================================================
// File: JPGRAPH_ERROR.PHP
// Description: Error plot extension for JpGraph
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_error.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
//===================================================
// CLASS ErrorPlot
// Description: Error plot with min/max value for
// each datapoint
//===================================================
class ErrorPlot extends Plot {
var $errwidth=2;
//---------------
// CONSTRUCTOR
function ErrorPlot(&$datay,$datax=false) {
$this->Plot($datay,$datax);
$this->numpoints /= 2;
}
//---------------
// PUBLIC METHODS
// Gets called before any axis are stroked
function PreStrokeAdjust(&$graph) {
if( $this->center ) {
$a=0.5; $b=0.5;
++$this->numpoints;
} else {
$a=0; $b=0;
}
$graph->xaxis->scale->ticks->SetXLabelOffset($a);
$graph->SetTextScaleOff($b);
//$graph->xaxis->scale->ticks->SupressMinorTickMarks();
}
// Method description
function Stroke(&$img,&$xscale,&$yscale) {
$numpoints=count($this->coords[0])/2;
$img->SetColor($this->color);
$img->SetLineWeight($this->weight);
 
if( isset($this->coords[1]) ) {
if( count($this->coords[1])!=$numpoints )
JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
else
$exist_x = true;
}
else
$exist_x = false;
 
if( $exist_x )
$xs=$this->coords[1][0];
else
$xs=0;
 
for( $i=0; $i<$numpoints; ++$i) {
if( $exist_x ) $x=$this->coords[1][$i];
else $x=$i;
$xt = $xscale->Translate($x);
$yt1 = $yscale->Translate($this->coords[0][$i*2]);
$yt2 = $yscale->Translate($this->coords[0][$i*2+1]);
$img->Line($xt,$yt1,$xt,$yt2);
$img->Line($xt-$this->errwidth,$yt1,$xt+$this->errwidth,$yt1);
$img->Line($xt-$this->errwidth,$yt2,$xt+$this->errwidth,$yt2);
}
return true;
}
} // Class
 
 
//===================================================
// CLASS ErrorLinePlot
// Description: Combine a line and error plot
// THIS IS A DEPRECATED PLOT TYPE JUST KEPT FOR
// BACKWARD COMPATIBILITY
//===================================================
class ErrorLinePlot extends ErrorPlot {
var $line=null;
//---------------
// CONSTRUCTOR
function ErrorLinePlot(&$datay,$datax=false) {
$this->ErrorPlot($datay,$datax);
// Calculate line coordinates as the average of the error limits
for($i=0; $i < count($datay); $i+=2 ) {
$ly[]=($datay[$i]+$datay[$i+1])/2;
}
$this->line=new LinePlot($ly,$datax);
}
 
//---------------
// PUBLIC METHODS
function Legend(&$graph) {
if( $this->legend != "" )
$graph->legend->Add($this->legend,$this->color);
$this->line->Legend($graph);
}
function Stroke(&$img,&$xscale,&$yscale) {
parent::Stroke($img,$xscale,$yscale);
$this->line->Stroke($img,$xscale,$yscale);
}
} // Class
 
 
//===================================================
// CLASS LineErrorPlot
// Description: Combine a line and error plot
//===================================================
class LineErrorPlot extends ErrorPlot {
var $line=null;
//---------------
// CONSTRUCTOR
// Data is (val, errdeltamin, errdeltamax)
function LineErrorPlot(&$datay,$datax=false) {
$ly=array(); $ey=array();
$n = count($datay);
if( $n % 3 != 0 ) {
JpGraphError::Raise('Error in input data to LineErrorPlot.'.
'Number of data points must be a multiple of 3');
}
for($i=0; $i < count($datay); $i+=3 ) {
$ly[]=$datay[$i];
$ey[]=$datay[$i]+$datay[$i+1];
$ey[]=$datay[$i]+$datay[$i+2];
}
$this->ErrorPlot($ey,$datax);
$this->line=new LinePlot($ly,$datax);
}
 
//---------------
// PUBLIC METHODS
function Legend(&$graph) {
if( $this->legend != "" )
$graph->legend->Add($this->legend,$this->color);
$this->line->Legend($graph);
}
function Stroke(&$img,&$xscale,&$yscale) {
parent::Stroke($img,$xscale,$yscale);
$this->line->Stroke($img,$xscale,$yscale);
}
} // Class
 
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/imgdata_stars.inc
New file
0,0 → 1,146
<?php
//=======================================================================
// File: IMGDATA_STARS.INC
// Description: Base64 encoded images for stars
// Created: 2003-03-20
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: imgdata_stars.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
 
class ImgData_Stars extends ImgData {
var $name = 'Stars';
var $an = array(MARK_IMG_STAR => 'imgdata');
 
var $colors = array('bluegreen','lightblue','purple','blue','green','pink','red','yellow');
var $index = array('bluegreen'=>3,'lightblue'=>4,'purple'=>1,
'blue'=>5,'green'=>0,'pink'=>7,'red'=>2,'yellow'=>6);
var $maxidx = 7 ;
var $imgdata ;
 
function ImgData_Stars() {
//==========================================================
// File: bstar_green_001.png
//==========================================================
$this->imgdata[0][0]= 329 ;
$this->imgdata[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAAUV'.
'BMVEX///////+/v7+83rqcyY2Q/4R7/15y/1tp/05p/0lg/zdX'.
'/zdX/zVV/zdO/zFJ9TFJvDFD4yg+8Bw+3iU68hwurhYotxYosx'.
'YokBoTfwANgQFUp7DWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJj'.
'CRyxgTAAAAcUlEQVR4nH3MSw6AIAwEUBL/IKBWwXL/g0pLojUS'.
'ZzGLl8ko9Zumhr5iy66/GH0dp49llNPB5sTotDY5PVuLG6tnM9'.
'CVKSIe1joSgPsAKSuANNaENFQvTAGzmheSkUpMBWeJZwqBT8wo'.
'hmysD4bnnPsC/x8ItUdGPfAAAAAASUVORK5CYII=' ;
//==========================================================
// File: bstar_blred.png
//==========================================================
$this->imgdata[1][0]= 325 ;
$this->imgdata[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v79uRJ6jWPOSUtKrb+ejWO+gWPaGTruJTr6rZvF2'.
'RqC2ocqdVuCeV+egV/GsnLuIXL66rMSpcOyATbipY/OdWOp+VK'.
'aTU9WhV+yJKBoLAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJwynv1'.
'XVAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bstar_red_001.png
//==========================================================
$this->imgdata[2][0]= 325 ;
$this->imgdata[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v7+eRFHzWG3SUmHnb37vWGr2WHG7Tlm+TljxZneg'.
'Rk3KoaXgVmXnV2nxV227nJ++XGzErK3scIS4TVzzY3fqWG2mVF'.
'zVU2PsV2rJFw9VAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJzCI0C'.
'lSAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bstar_blgr_001.png
//==========================================================
$this->imgdata[3][0]= 325 ;
$this->imgdata[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v79Ehp5Yx/NSq9Jvw+dYwu9YzfZOmbtOmb5myPFG'.
'gqChvcpWteBXvedXxvGcsbtcpb6su8RwzOxNmrhjyvNYwupUjK'.
'ZTr9VXwOyJhmWNAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJTC65k'.
'vQAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bstar_blgr_002.png
//==========================================================
$this->imgdata[4][0]= 325 ;
$this->imgdata[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v79EnpxY8/FS0dJv5+dY7+9Y9vBOubtOur5m8fFG'.
'nKChycpW3uBX5+ZX8e2curtcvrqswsRw7OdNuLZj8/BY6udUpK'.
'ZT1dRX7OtNkrW5AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJgXHeN'.
'wwAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bstar_blue_001.png
//==========================================================
$this->imgdata[5][0]= 325 ;
$this->imgdata[5][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v79EY55Yi/NSetJvledYiO9YkPZOb7tObr5mkvFG'.
'X6ChrcpWgOBXhedXi/Gcpbtcf76sssRwnOxNcbhjk/NYiepUbK'.
'ZTfdVXh+ynNEzzAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJhStyP'.
'zCAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bstar_oy_007.png
//==========================================================
$this->imgdata[6][0]= 325 ;
$this->imgdata[6][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v7+ejUTz11jSvVLn02/v1lj21li7q06+r07x2mag'.
'lUbKxKHgy1bnz1fx1Ve7t5y+qlzEwqzs03C4pE3z2WPqz1imml'.
'TVv1Ps01dGRjeyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJjsGGc'.
'GbAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bstar_lred.png
//==========================================================
$this->imgdata[7][0]= 325 ;
$this->imgdata[7][1]=
'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
'BMVEX///+/v7+eRJPzWN3SUr7nb9TvWNj2WOS7Tqi+TqnxZtyg'.
'Ro/KocPgVsjnV9LxV927nLa+XLTErL7scN24TarzY9/qWNemVJ'.
'jVU8LsV9VCwcc9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJxi9ZY'.
'GoAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
}
}
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_radar.php
New file
0,0 → 1,576
<?php
/*=======================================================================
// File: JPGRAPH_RADAR.PHP
// Description: Radar plot extension for JpGraph
// Created: 2001-02-04
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_radar.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
 
class RadarLogTicks extends Ticks {
//---------------
// CONSTRUCTOR
function RadarLogTicks() {
}
//---------------
// PUBLIC METHODS
 
// TODO: Add Argument grid
function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
$start = $aScale->GetMinVal();
$limit = $aScale->GetMaxVal();
$nextMajor = 10*$start;
$step = $nextMajor / 10.0;
$count=1;
$ticklen_maj=5;
$dx_maj=round(sin($aAxisAngle)*$ticklen_maj);
$dy_maj=round(cos($aAxisAngle)*$ticklen_maj);
$ticklen_min=3;
$dx_min=round(sin($aAxisAngle)*$ticklen_min);
$dy_min=round(cos($aAxisAngle)*$ticklen_min);
$aMajPos=array();
$aMajLabel=array();
if( $this->supress_first )
$aMajLabel[]="";
else
$aMajLabel[]=$start;
$yr=$aScale->RelTranslate($start);
$xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
$yt=$aPos-round($yr*sin($aAxisAngle));
$aMajPos[]=$xt+2*$dx_maj;
$aMajPos[]=$yt-$aImg->GetFontheight()/2;
$grid[]=$xt;
$grid[]=$yt;
 
$aImg->SetLineWeight($this->weight);
for($y=$start; $y<=$limit; $y+=$step,++$count ) {
$yr=$aScale->RelTranslate($y);
$xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
$yt=$aPos-round($yr*sin($aAxisAngle));
if( $count % 10 == 0 ) {
$grid[]=$xt;
$grid[]=$yt;
$aMajPos[]=$xt+2*$dx_maj;
$aMajPos[]=$yt-$aImg->GetFontheight()/2;
if( !$this->supress_tickmarks ) {
if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
$aImg->Line($xt+$dx_maj,$yt+$dy_maj,$xt-$dx_maj,$yt-$dy_maj);
if( $this->majcolor!="" ) $aImg->PopColor();
}
$aMajLabel[]=$nextMajor;
$nextMajor *= 10;
$step *= 10;
$count=1;
}
else
if( !$this->supress_minor_tickmarks ) {
if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
$aImg->Line($xt+$dx_min,$yt+$dy_min,$xt-$dx_min,$yt-$dy_min);
if( $this->mincolor!="" ) $aImg->PopColor();
}
}
}
}
class RadarLinearTicks extends LinearTicks {
//---------------
// CONSTRUCTOR
function RadarLinearTicks() {
// Empty
}
 
//---------------
// PUBLIC METHODS
 
// TODO: Add argument grid
function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
// Prepare to draw linear ticks
$maj_step_abs = abs($aScale->scale_factor*$this->major_step);
$min_step_abs = abs($aScale->scale_factor*$this->minor_step);
$nbrmaj = floor(($aScale->world_abs_size)/$maj_step_abs);
$nbrmin = floor(($aScale->world_abs_size)/$min_step_abs);
$skip = round($nbrmin/$nbrmaj); // Don't draw minor ontop of major
 
// Draw major ticks
$ticklen2=$this->major_abs_size;
$dx=round(sin($aAxisAngle)*$ticklen2);
$dy=round(cos($aAxisAngle)*$ticklen2);
$label=$aScale->scale[0]+$this->major_step;
$aImg->SetLineWeight($this->weight);
for($i=1; $i<=$nbrmaj; ++$i) {
$xt=round($i*$maj_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
$yt=$aPos-round($i*$maj_step_abs*sin($aAxisAngle));
$aMajLabel[]=$label;
$label += $this->major_step;
$grid[]=$xt;
$grid[]=$yt;
$aMajPos[($i-1)*2]=$xt+2*$dx;
$aMajPos[($i-1)*2+1]=$yt-$aImg->GetFontheight()/2;
if( !$this->supress_tickmarks ) {
if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
$aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
if( $this->majcolor!="" ) $aImg->PopColor();
}
}
 
// Draw minor ticks
$ticklen2=$this->minor_abs_size;
$dx=round(sin($aAxisAngle)*$ticklen2);
$dy=round(cos($aAxisAngle)*$ticklen2);
if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
for($i=1; $i<=$nbrmin; ++$i) {
if( ($i % $skip) == 0 ) continue;
$xt=round($i*$min_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
$yt=$aPos-round($i*$min_step_abs*sin($aAxisAngle));
$aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
}
if( $this->mincolor!="" ) $aImg->PopColor();
}
}
}
 
 
//===================================================
// CLASS RadarAxis
// Description: Implements axis for the spider graph
//===================================================
class RadarAxis extends Axis {
var $title_color="navy";
var $title=null;
//---------------
// CONSTRUCTOR
function RadarAxis(&$img,&$aScale,$color=array(0,0,0)) {
parent::Axis($img,$aScale,$color);
$this->len=$img->plotheight;
$this->title = new Text();
$this->title->SetFont(FF_FONT1,FS_BOLD);
$this->color = array(0,0,0);
}
//---------------
// PUBLIC METHODS
function SetTickLabels($l) {
$this->ticks_label = $l;
}
// Stroke the axis
// $pos = Vertical position of axis
// $aAxisAngle = Axis angle
// $grid = Returns an array with positions used to draw the grid
// $lf = Label flag, TRUE if the axis should have labels
function Stroke($pos,$aAxisAngle,&$grid,$title,$lf) {
$this->img->SetColor($this->color);
// Determine end points for the axis
$x=round($this->scale->world_abs_size*cos($aAxisAngle)+$this->scale->scale_abs[0]);
$y=round($pos-$this->scale->world_abs_size*sin($aAxisAngle));
// Draw axis
$this->img->SetColor($this->color);
$this->img->SetLineWeight($this->weight);
if( !$this->hide )
$this->img->Line($this->scale->scale_abs[0],$pos,$x,$y);
$this->scale->ticks->Stroke($this->img,$grid,$pos,$aAxisAngle,$this->scale,$majpos,$majlabel);
// Draw labels
if( $lf && !$this->hide ) {
$this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
$this->img->SetTextAlign("left","top");
$this->img->SetColor($this->color);
// majpos contsins (x,y) coordinates for labels
for($i=0; $i<count($majpos)/2; ++$i) {
if( $this->ticks_label != null )
$this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$this->ticks_label[$i]);
else
$this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$majlabel[$i]);
}
}
$this->_StrokeAxisTitle($pos,$aAxisAngle,$title);
}
//---------------
// PRIVATE METHODS
function _StrokeAxisTitle($pos,$aAxisAngle,$title) {
$this->title->Set($title);
$marg=6+$this->title->margin;
$xt=round(($this->scale->world_abs_size+$marg)*cos($aAxisAngle)+$this->scale->scale_abs[0]);
$yt=round($pos-($this->scale->world_abs_size+$marg)*sin($aAxisAngle));
 
// Position the axis title.
// dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
// that intersects with the extension of the corresponding axis. The code looks a little
// bit messy but this is really the only way of having a reasonable position of the
// axis titles.
$h=$this->img->GetFontHeight();
$w=$this->img->GetTextWidth($title);
while( $aAxisAngle > 2*M_PI ) $aAxisAngle -= 2*M_PI;
if( $aAxisAngle>=7*M_PI/4 || $aAxisAngle <= M_PI/4 ) $dx=0;
if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dx=($aAxisAngle-M_PI/4)*2/M_PI;
if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dx=1;
if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dx=(1-($aAxisAngle-M_PI*5/4)*2/M_PI);
if( $aAxisAngle>=7*M_PI/4 ) $dy=(($aAxisAngle-M_PI)-3*M_PI/4)*2/M_PI;
if( $aAxisAngle<=M_PI/4 ) $dy=(1-$aAxisAngle*2/M_PI);
if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dy=1;
if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dy=(1-($aAxisAngle-3*M_PI/4)*2/M_PI);
if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dy=0;
if( !$this->hide )
$this->title->Stroke($this->img,$xt-$dx*$w,$yt-$dy*$h,$title);
}
} // Class
 
 
//===================================================
// CLASS RadarGrid
// Description: Draws grid for the spider graph
//===================================================
class RadarGrid extends Grid {
//------------
// CONSTRUCTOR
function RadarGrid() {
}
 
//----------------
// PRIVATE METHODS
function Stroke(&$img,&$grid) {
if( !$this->show ) return;
$nbrticks = count($grid[0])/2;
$nbrpnts = count($grid);
$img->SetColor($this->grid_color);
$img->SetLineWeight($this->weight);
for($i=0; $i<$nbrticks; ++$i) {
for($j=0; $j<$nbrpnts; ++$j) {
$pnts[$j*2]=$grid[$j][$i*2];
$pnts[$j*2+1]=$grid[$j][$i*2+1];
}
for($k=0; $k<$nbrpnts; ++$k ){
$l=($k+1)%$nbrpnts;
if( $this->type == "solid" )
$img->Line($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1]);
elseif( $this->type == "dotted" )
$img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],1,6);
elseif( $this->type == "dashed" )
$img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],2,4);
elseif( $this->type == "longdashed" )
$img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],8,6);
}
$pnts=array();
}
}
} // Class
 
 
//===================================================
// CLASS RadarPlot
// Description: Plot a spiderplot
//===================================================
class RadarPlot {
var $data=array();
var $fill=false, $fill_color=array(200,170,180);
var $color=array(0,0,0);
var $legend="";
var $weight=1;
//---------------
// CONSTRUCTOR
function RadarPlot($data) {
$this->data = $data;
}
 
//---------------
// PUBLIC METHODS
function Min() {
return Min($this->data);
}
function Max() {
return Max($this->data);
}
function SetLegend($legend) {
$this->legend=$legend;
}
function SetLineWeight($w) {
$this->weight=$w;
}
function SetFillColor($aColor) {
$this->fill_color = $aColor;
$this->fill = true;
}
function SetFill($f=true) {
$this->fill = $f;
}
function SetColor($aColor,$aFillColor=false) {
$this->color = $aColor;
if( $aFillColor ) {
$this->SetFillColor($aFillColor);
$this->fill = true;
}
}
function GetCSIMareas() {
JpGraphError::Raise("Client side image maps not supported for RadarPlots.");
}
function Stroke(&$img, $pos, &$scale, $startangle) {
$nbrpnts = count($this->data);
$astep=2*M_PI/$nbrpnts;
$a=$startangle;
// Rotate each point to the correct axis-angle
// TODO: Update for LogScale
for($i=0; $i<$nbrpnts; ++$i) {
//$c=$this->data[$i];
$cs=$scale->RelTranslate($this->data[$i]);
$x=round($cs*cos($a)+$scale->scale_abs[0]);
$y=round($pos-$cs*sin($a));
/*
$c=log10($c);
$x=round(($c-$scale->scale[0])*$scale->scale_factor*cos($a)+$scale->scale_abs[0]);
$y=round($pos-($c-$scale->scale[0])*$scale->scale_factor*sin($a));
*/
$pnts[$i*2]=$x;
$pnts[$i*2+1]=$y;
$a += $astep;
}
if( $this->fill ) {
$img->SetColor($this->fill_color);
$img->FilledPolygon($pnts);
}
$img->SetLineWeight($this->weight);
$img->SetColor($this->color);
$pnts[]=$pnts[0];
$pnts[]=$pnts[1];
$img->Polygon($pnts);
}
//---------------
// PRIVATE METHODS
function GetCount() {
return count($this->data);
}
function Legend(&$graph) {
if( $this->legend=="" ) return;
if( $this->fill )
$graph->legend->Add($this->legend,$this->fill_color);
else
$graph->legend->Add($this->legend,$this->color);
}
} // Class
 
//===================================================
// CLASS RadarGraph
// Description: Main container for a spider graph
//===================================================
class RadarGraph extends Graph {
var $posx;
var $posy;
var $len;
var $plots=null, $axis_title=null;
var $grid,$axis=null;
//---------------
// CONSTRUCTOR
function RadarGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
$this->Graph($width,$height,$cachedName,$timeout,$inline);
$this->posx=$width/2;
$this->posy=$height/2;
$this->len=min($width,$height)*0.35;
$this->SetColor(array(255,255,255));
$this->SetTickDensity(TICKD_NORMAL);
$this->SetScale("lin");
}
 
//---------------
// PUBLIC METHODS
function SupressTickMarks($f=true) {
if( ERR_DEPRECATED )
JpGraphError::Raise('RadarGraph::SupressTickMarks() is deprecated. Use HideTickMarks() instead.');
$this->axis->scale->ticks->SupressTickMarks($f);
}
 
function HideTickMarks($aFlag=true) {
$this->axis->scale->ticks->SupressTickMarks($aFlag);
}
function ShowMinorTickmarks($aFlag=true) {
$this->yscale->ticks->SupressMinorTickMarks(!$aFlag);
}
function SetScale($axtype,$ymin=1,$ymax=1) {
if( $axtype != "lin" && $axtype != "log" ) {
JpGraphError::Raise("Illegal scale for spiderplot ($axtype). Must be \"lin\" or \"log\"");
}
if( $axtype=="lin" ) {
$this->yscale = & new LinearScale($ymin,$ymax);
$this->yscale->ticks = & new RadarLinearTicks();
$this->yscale->ticks->SupressMinorTickMarks();
}
elseif( $axtype=="log" ) {
$this->yscale = & new LogScale($ymin,$ymax);
$this->yscale->ticks = & new RadarLogTicks();
}
$this->axis = & new RadarAxis($this->img,$this->yscale);
$this->grid = & new RadarGrid();
}
 
function SetSize($aSize) {
if( $aSize<0.1 || $aSize>1 )
JpGraphError::Raise("Radar Plot size must be between 0.1 and 1. (Your value=$s)");
$this->len=min($this->img->width,$this->img->height)*$aSize/2;
}
 
function SetPlotSize($aSize) {
$this->SetSize($aSize);
}
 
function SetTickDensity($densy=TICKD_NORMAL) {
$this->ytick_factor=25;
switch( $densy ) {
case TICKD_DENSE:
$this->ytick_factor=12;
break;
case TICKD_NORMAL:
$this->ytick_factor=25;
break;
case TICKD_SPARSE:
$this->ytick_factor=40;
break;
case TICKD_VERYSPARSE:
$this->ytick_factor=70;
break;
default:
JpGraphError::Raise("RadarPlot Unsupported Tick density: $densy");
}
}
 
function SetPos($px,$py=0.5) {
$this->SetCenter($px,$py);
}
 
function SetCenter($px,$py=0.5) {
assert($px > 0 && $py > 0 );
$this->posx=$this->img->width*$px;
$this->posy=$this->img->height*$py;
}
 
function SetColor($c) {
$this->SetMarginColor($c);
}
function SetTitles($title) {
$this->axis_title = $title;
}
 
function Add(&$splot) {
$this->plots[]=$splot;
}
function GetPlotsYMinMax() {
$min=$this->plots[0]->Min();
$max=$this->plots[0]->Max();
foreach( $this->plots as $p ) {
$max=max($max,$p->Max());
$min=min($min,$p->Min());
}
if( $min < 0 )
JpGraphError::Raise("Minimum data $min (Radar plots only makes sence to use when all data points > 0)");
return array($min,$max);
}
 
// Stroke the Radar graph
function Stroke($aStrokeFileName="") {
// Set Y-scale
if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
list($min,$max) = $this->GetPlotsYMinMax();
$this->yscale->AutoScale($this->img,0,$max,$this->len/$this->ytick_factor);
}
// Set start position end length of scale (in absolute pixels)
$this->yscale->SetConstants($this->posx,$this->len);
// We need as many axis as there are data points
$nbrpnts=$this->plots[0]->GetCount();
// If we have no titles just number the axis 1,2,3,...
if( $this->axis_title==null ) {
for($i=0; $i<$nbrpnts; ++$i )
$this->axis_title[$i] = $i+1;
}
elseif(count($this->axis_title)<$nbrpnts)
JpGraphError::Raise("Number of titles does not match number of points in plot.");
for($i=0; $i<count($this->plots); ++$i )
if( $nbrpnts != $this->plots[$i]->GetCount() )
JpGraphError::Raise("Each spider plot must have the same number of data points.");
 
if( $this->background_image != "" ) {
$this->StrokeFrameBackground();
}
else {
$this->StrokeFrame();
}
$astep=2*M_PI/$nbrpnts;
 
// Prepare legends
for($i=0; $i<count($this->plots); ++$i)
$this->plots[$i]->Legend($this);
$this->legend->Stroke($this->img);
$this->footer->Stroke($this->img);
// Plot points
$a=M_PI/2;
for($i=0; $i<count($this->plots); ++$i )
$this->plots[$i]->Stroke($this->img, $this->posy, $this->yscale, $a);
// Draw axis and grid
for( $i=0,$a=M_PI/2; $i<$nbrpnts; ++$i, $a+=$astep ) {
$this->axis->Stroke($this->posy,$a,$grid[$i],$this->axis_title[$i],$i==0);
}
$this->grid->Stroke($this->img,$grid);
$this->StrokeTitles();
// Stroke texts
if( $this->texts != null )
foreach( $this->texts as $t)
$t->Stroke($this->img);
// If the filename is given as the special "__handle"
// then the image handler is returned and the image is NOT
// streamed back
if( $aStrokeFileName == _IMG_HANDLER ) {
return $this->img->img;
}
else {
// Finally stream the generated picture
$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
$aStrokeFileName);
}
}
} // Class
 
/* EOF */
?>
/trunk/api/jpgraph_1.12.2/imgdata_balls.inc
New file
0,0 → 1,1065
<?php
//=======================================================================
// File: IMGDATA_ROUNDBALLS.INC
// Description: Base64 encoded images for small round markers
// Created: 2003-03-20
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: imgdata_balls.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
class ImgData_Balls extends ImgData {
var $name = 'Round Balls';
var $an = array(MARK_IMG_LBALL => 'imgdata_large',
MARK_IMG_MBALL => 'imgdata_small',
MARK_IMG_SBALL => 'imgdata_xsmall',
MARK_IMG_BALL => 'imgdata_xsmall');
var $colors_1 = array('blue','lightblue','brown','darkgreen',
'green','purple','red','gray','yellow','silver','gray');
var $index_1 = array('blue'=>9,'lightblue'=>1,'brown'=>6,'darkgreen'=>7,
'green'=>8,'purple'=>4,'red'=>0,'gray'=>5,'silver'=>3,'yellow'=>2);
var $maxidx_1 = 9 ;
 
var $colors_2 = array('blue','bluegreen','brown','cyan',
'darkgray','greengray','gray','green',
'greenblue','lightblue','lightred',
'purple','red','white','yellow');
var $index_2 = array('blue'=>9,'bluegreen'=>13,'brown'=>8,'cyan'=>12,
'darkgray'=>5,'greengray'=>6,'gray'=>2,'green'=>10,
'greenblue'=>3,'lightblue'=>1,'lightred'=>14,
'purple'=>7,'red'=>0,'white'=>11,'yellow'=>4);
var $maxidx_2 = 14 ;
 
 
var $colors_3 = array('bluegreen','cyan','darkgray','greengray',
'gray','graypurple','green','greenblue','lightblue',
'lightred','navy','orange','purple','red','yellow');
var $index_3 = array('bluegreen'=>1,'cyan'=>11,'darkgray'=>14,'greengray'=>10,
'gray'=>3,'graypurple'=>4,'green'=>9,'greenblue'=>7,
'lightblue'=>13,'lightred'=>0,'navy'=>2,'orange'=>12,
'purple'=>8,'red'=>5,'yellow'=>6);
var $maxidx_3 = 14 ;
 
var $colors,$index,$maxidx;
var $imgdata_large ;
var $imgdata_small ;
var $imgdata_xsmall ;
 
 
function GetImg($aMark,$aIdx) {
switch( $aMark ) {
case MARK_IMG_SBALL:
case MARK_IMG_BALL:
$this->colors = $this->colors_3;
$this->index = $this->index_3 ;
$this->maxidx = $this->maxidx_3 ;
break;
case MARK_IMG_MBALL:
$this->colors = $this->colors_2;
$this->index = $this->index_2 ;
$this->maxidx = $this->maxidx_2 ;
break;
default:
$this->colors = $this->colors_1;
$this->index = $this->index_1 ;
$this->maxidx = $this->maxidx_1 ;
break;
}
return parent::GetImg($aMark,$aIdx);
}
 
function ImgData_Balls() {
 
//==========================================================
// File: bl_red.png
//==========================================================
$this->imgdata_large[0][0]= 1072 ;
$this->imgdata_large[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAByF'.
'BMVEX/////////xsb/vb3/lIz/hIT/e3v/c3P/c2v/a2v/Y2P/'.
'UlL/Skr/SkL/Qjn/MTH/MSn/KSn/ISH/IRj/GBj/GBD/EBD/EA'.
'j/CAj/CAD/AAD3QkL3MTH3KSn3KSH3GBj3EBD3CAj3AAD1zMzv'.
'QkLvISHvIRjvGBjvEBDvEAjvAADnUlLnSkrnMTnnKSnnIRjnGB'.
'DnEBDnCAjnAADec3PeSkreISHeGBjeGBDeEAjWhITWa2vWUlLW'.
'SkrWISnWGBjWEBDWEAjWCAjWAADOnp7Oa2vOGCHOGBjOGBDOEB'.
'DOCAjOAADJrq7Gt7fGGBjGEBDGCAjGAADEpKS/v7+9QkK9GBC9'.
'EBC9CAi9AAC1e3u1a2u1Skq1KSm1EBC1CAi1AACtEBCtCBCtCA'.
'itAACngYGlCAilAACghIScOTmcCAicAACYgYGUGAiUCAiUAAiU'.
'AACMKSmMEACMAACEa2uEGAiEAAB7GBh7CAB7AABzOTlzGBBzCA'.
'BzAABrSkprOTlrGBhrAABjOTljAABaQkJaOTlaCABaAABSKSlS'.
'GBhSAABKKSlKGBhKAABCGBhCCABCAAA5CAA5AAAxCAAxAAApCA'.
'ApAAAhAAAYAACc9eRyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkRFD'.
'UHLytKAAAB4UlEQVR4nGNgIAK4mGjrmNq6BmFIWMmISUpKSmk5'.
'B8ZEokj4qoiLiQCBgqald3xaBpKMj6y4sLCQkJCIvIaFV0RaUR'.
'lCSk5cWEiAn19ASN7QwisuraihHiajKyEixM/NwckjoKrvEACU'.
'qumpg7pAUlREiJdNmZmLT9/cMzwps7Smc3I2WEpGUkxYkJuFiY'.
'lTxszePzY1v7Shc2oX2D+K4iLCgjzsrOw8embuYUmZeTVtPVOn'.
'gqSslYAOF+Ln4ZHWtXMPTcjMrWno7J82rRgoZWOsqaCgrqaqqm'.
'fn5peQmlsK1DR52vRaoFSIs5GRoYG5ub27n19CYm5pdVPnxKnT'.
'pjWDpLydnZwcHTz8QxMSEnJLgDL9U6dNnQ6Sio4PDAgICA+PTU'.
'zNzSkph8hADIxKS46Pj0tKTc3MLSksqWrtmQySAjuDIT8rKy0r'.
'Kz+vtLSmur6jb9JUIJgGdjxDQUVRUVFpaUVNQ1NrZ9+kKVOmTZ'.
'k6vR0sldJUAwQNTU2dnX0TgOJTQLrSIYFY2dPW1NbW2TNxwtQp'.
'U6ZMmjJt2rRGWNB3TO7vnzh5MsgSoB6gy7sREdY7bRrQEDAGOb'.
'wXOQW0TJsOEpwClmxBTTbZ7UDVIPkp7dkYaYqhuLa5trYYUxwL'.
'AADzm6uekAAcXAAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bl_bluegreen.png
//==========================================================
$this->imgdata_large[1][0]= 1368 ;
$this->imgdata_large[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMMFi8hE9b2uAAABOVJREFUeNq9lk2sJFUVx3+3qv'.
'tW95t57zFvhiFxmCFRUJRoNCQiJARMhiFx/Igxii5goTG6ZDAu'.
'/EhcSCIrTAgLEiKsJ8ywABNZEMJXEDYCukAmjgjzBkK/j35V1d'.
'333FtV97io97pfzwxfG86qcu/N+Z3zP+fcW/Apmfk4hx57+R/6'.
'Rqmc9ykhsWjlsUngAA1fXIQ7b73pI/186IGHnn9dH/8frC8v4I'.
'PiG53uaerR4GmKkv31mB8cyfjd946ZTwR66qVX9OTWIi8UKUv9'.
'BOrZXpYZvFeiBvzI0fgSUSFKwbVG+Pl1V3HH0VvNR4KeeukV/f'.
'PmMmdHhst76aXD64AbeVQ9bjNHaiGOC2o3wLrAb2/4LL/84ffn'.
'fCdzkOdayKpLppBemrBsU5Y1Zdmm9LJdGU6E/t4M24Q26jRDRL'.
'j3mdc49cSTekFsMzs5XuTsyLDUNSDQ25NwKOly9YIl22MYhJr/'.
'uoDtBBoT0CxBRGYOAhibIaOCe//2MpfM6KHnX9cXipSlbkKWmS'.
'nk9iv38J0jixw7vJfrTMYBOvhSoQHJBS09ANELloAGDxW8tfoW'.
'J+5/UC8CPS0LU7r3SpYarr7M8rmFjMPLXT6/33L4si7Z2GCrQC'.
'+0ctlOaNs9DReV8vSLr85ndPLpZ/WNvHW+01kAVFBOGvJx0wYg'.
'Sp47RIQ4Emwa8FGJXlDxSCFo5YlVgAo2hwPue/hRndboTV3EW2'.
'Wp3k6wBp8q56QiWzecW6vwQfnPRkAWhFgILnq08jQ+R2nlUzzN'.
'uES9Q7Vd+9fba7NmWJW61db2247qACmcjxXr45psYphsFGSLBu'.
'kIajxqtjNwHkvAjQt0sg3crhPA2+fPz0CuyNFOghsGsr19mnFg'.
'DGwrRm8UoAtNmQPQtRXDgdC4HImCFEKcCE0oieUWUYq2LtbiGp'.
'mBQmppfIkjw45DK0QNNkvQ0jMBtPL0UnDRM1rN+cxKwzvOo2NP'.
'tykR9a1kfpZNDLMG6QDYJqCTBvUe1+uxs+YKyPoGrTwY2HhvC4'.
'CDWQd5d4xNApNQEEMgjgLdUCLBQ5cprL/trwNwKG2IUmDqDFd5'.
'sr5BWrlxuSdLDFEFlqAzXGc4zFjupqh6uqYihpxJcEgp026l2w'.
'7wFUv7Z6AvrfRo/n0OYzPwIKE3HUKAJg2otMBiElnsF7wngis9'.
'3ZDjNnLi7huCWUZfueZKTu/M0V3HvmkOFDVxVKDG04ScejSgW5'.
'V0q5JYFEghuDLHlTmToqDeGOCKIVtrW9hsdmXufEcNLPSXuPHa'.
'a+bvuh9df5AH/v5PDFmbWQC3Mx+TVvfGVTRB2CodNgT2JBX003'.
'aANZAYS/BxCv32TV/l2C03G7jgmfjGiT/qmeEmibEYm7XzAO2k'.
'A+pbgHhBgydqu54YO5eRiLCy7yDvPP6Xqf+5Z+Lu277OYuOpiw'.
'H15oBmlNOMcmK5RbP+PrEscGU+DSAxdg4CICIkxnLP8aNz63Og'.
'H3/rdvOb795GVhuaYo0oBc3GGrEsUPVTwO6a7LYd+X51x3Hu/t'.
'lP5tS65FN+6okn9U+n/sqb596dTvhOF+02myXTmkQNrOw7yD3H'.
'j14E+UDQjp24/0E9/eKrbA4HH3aMK1b2ccvXvswjv//1J/s5ud'.
'Due/hRPfP+OmfOrk7vrn7a48ihA3zh8CH+8Iuffiw/n4r9H1ZZ'.
'0zz7G56hAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bl_yellow.png
//==========================================================
$this->imgdata_large[2][0]= 1101 ;
$this->imgdata_large[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAB2l'.
'BMVEX//////////+///+f//9b//8b//73//7X//63//6X//5T/'.
'/4z//4T//3P//2v//1r//0r//0L//zH//yn//yH//xj//xD//w'.
'j//wD/90L/9zn/9zH/9xj/9xD/9wj/9wD39yn37zn37zH37yH3'.
'7xD37wj37wDv70Lv50rv50Lv5znv5yHv5xjv5wjv5wDn51Ln5x'.
'Dn3jHn3iHn3hjn3hDn3gje3oze3nPe3lLe1oze1nPe1lLe1ine'.
'1iHe1hje1hDe1gje1gDW1qXW1mvWzqXWzkLWzhjWzhDWzgjWzg'.
'DOzrXOzq3OzpzOzgDOxkrOxinOxhjOxhDOxgjOxgDGxqXGxnvG'.
'xmvGvRjGvRDGvQjGvQDFxbnAvr6/v7+9vaW9vZS9vQi9vQC9tR'.
'C9tQi9tQC7u7W1tZS1tXu1tTG1tQi1rRC1rQi1rQCtrYytrSGt'.
'rQitrQCtpYStpSGtpQitpQClpYSlpXulpQClnBClnAilnACcnG'.
'ucnAicnACclAiclACUlFqUlCmUlAiUlACUjFKUjAiUjACMjFKM'.
'jEqMjACMhACEhACEewB7ezF7exB7ewB7cwBzcylzcwBzaxBzaw'.
'BraxhrawhrawBrYxBrYwBjYwBjWgBaWgBaUgCXBwRMAAAAAXRS'.
'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
'LdfvwAAAAHdElNRQfTAwkRFBKiJZ4hAAAB7ElEQVR4nI3S+1vS'.
'UBgHcB67WJmIMWAVdDHEDLBC6Go0slj3Ft0m9RRBWQEmFZFDEM'.
'Qgt0EMFBY7p/+198hj1kM/9N1+++x73rOd6XT/kStnTx4fPzd9'.
'uwfOjFhomj7smAhwj/6Cm2O0xUwy6g7cCL99uCW3jtBmE7lsdr'.
'fvejgpzP7uEDFRRoqy2k8xQPnypo2BUMP6waF9Vpf3ciiSzErL'.
'XTkPc0zDe3bsHDAcc00yoVgqL3UWN2iENpspff+2vn6D0+NnZ9'.
'6lC5K6RuSqBTZn1O/a3rd7v/MSez+WyIpVFX8GuuCA9SjD4N6B'.
'oRNTfo5PCAVR0fBXoIuOQzab1XjwwNHx00GOj8/nKtV1DdeArk'.
'24R+0ul9PjmbrHPYl+EipyU0OoQSjg8/m83kl/MMhx0fjCkqio'.
'SMOE7t4JMAzDsizH81AqSdW2hroLPg4/CEF4PhKNx98vlevrbY'.
'QQXgV6kXwVfjkTiSXmhYVcSa7DIE1DOENe7GM6lUym0l+EXKks'.
'K20VAeH2M0JvVgrZfL5Qqkiy0lRVaMBd7H7EZUmsiJJcrTdVja'.
'wGpdbTLj3/3qwrUOjAfGgg4LnNA5tdQx14Hm00QFBm65hfNzAm'.
'+yIFhFtzuj+z2MI/MQn6Uez5pz4Ua41G7VumB/6RX4zMr1TKBr'.
'SXAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bl_silver.png
//==========================================================
$this->imgdata_large[3][0]= 1481 ;
$this->imgdata_large[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAADAF'.
'BMVEUAAADOzs7Gxsa9vb21tbXOxsbOzsbGzsb3///O1ta1vb2c'.
'paVSWlpKWlpSY2ve5+97hIze7/9aY2vO5/9zhJRaa3tSY3PGzt'.
'aMlJxrc3tja3NKUlpCSlK1vcZze4RSWmPW5/+Upb3G3v9zhJxS'.
'Y3t7jKVaa4TO3veltc6ElK1re5Rjc4ycpbV7hJRaY3M5QlLn7/'.
'/Gzt6lrb2EjJzO3v9ja3vG1ve9zu+1xueltdacrc6UpcaMnL1C'.
'SlqElLV7jK1zhKVre5zW3u/O1ue1vc6ttcaMlKVze4xrc4RSWm'.
'tKUmPG1v+9zve1xu+tveeltd6crdbe5/+9xt6cpb17hJxaY3s5'.
'QlrW3vfO1u/Gzue1vdattc6lrcaUnLWMlK2EjKVze5Rrc4xja4'.
'RSWnNKUmtCSmO9xuecpcZ7hKVaY4TW3v/O1vfGzu+1vd6ttdal'.
'rc69xu+UnL2MlLWEjK1ze5xrc5R7hK1ja4zO1v+1veettd6lrd'.
'aMlL3Gzv/39//W1t7Gxs61tb29vcatrbWlpa2cnKWUlJyEhIx7'.
'e4TW1ufGxta1tcZSUlqcnK3W1u+UlKW9vda1tc57e4ytrcalpb'.
'1ra3vOzu9jY3OUlK29vd6MjKWEhJxaWmtSUmNzc4xKSlpjY3tK'.
'SmNCQlqUjJzOxs7///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
'AAAAAAAAAAAAAAAAAAAAAAAAD///9fnkWVAAAAAnRSTlP/AOW3'.
'MEoAAAABYktHRP+lB/LFAAAACXBIWXMAAABFAAAARQBP+QatAA'.
'AB/klEQVR42mNgxAsYqCdd3+lcb4hLmj8wMMvEu8DCMqYbU9op'.
'UEFB2MTb26eyysomFl06XEEhUCHLpAKo2z/fujikEUVaXUFBMB'.
'BouLePuV+VVWGRciIXknSEsImCQd3//xwmPr65llaFcSFJHkjS'.
'3iYmWUDZ//8NfCr989NjNUMSUyTg0jneSiaCINn/gmlVQM12qg'.
'lJnp5waTMTE5NAkCyHWZW/lXWNfUlikmdYK0zax7siS4EDKJtd'.
'mQeU1XRwLBdLkRGASucWmGVnZ4dnhZvn5lmm29iVOWpnJqcuko'.
'JKR1Wm5eTkRKYF5eblp9sU2ZeUJiV7zbfVg0pH56UFBQXNjIqK'.
'jgkujItX1koKTVmYajsdKu2qETVhwgSXiUDZ2Bn9xqUeoZ5e0t'.
'LzYYZ3B092ndjtOnmKTmycW1s7SHa+l5dtB8zlccE6RlN0dGbM'.
'mDVbd5KupNBcL6+F82XgHouLj5vRP2PWLGNdd4+ppnxe8tJec6'.
'XnNsKkm0uVQ5RDRHQTPTym68nPlZbvkfYCexsa5rpJ2qXa5Umm'.
'ocmec3m8vHjmSs+fgxyhC5JDQ8WSPT2lvbzm8vDIe0nbtiBLN8'.
'8BigNdu1B6Lsje+fPbUFMLi5TMfGmvHi/puUAv23q2YCTFNqH5'.
'MvPnSwPh3HasCbm3XUpv+nS5VtrkEkwAANSTpGHdye9PAAAASn'.
'RFWHRzaWduYXR1cmUANGJkODkyYmE4MWZhNTk4MTIyNDJjNjUx'.
'NzZhY2UxMDAzOGFhZjdhZWIyNzliNTM2ZGFmZDlkM2RiNDU3Zm'.
'NlNT9CliMAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bl_purple.png
//==========================================================
$this->imgdata_large[4][0]= 1149 ;
$this->imgdata_large[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAACAV'.
'BMVEX/////////7///5///1v//xv//rf//pf//lP//jP//hP//'.
'c///a///Wv//Wvf/Uv//Sv//Qv//Qvf/Off/Mf//Kf//If//If'.
'f/GP//GPf/EP//EPf/CP//CPf/CO//AP//APf3Oe/3Kff3Ke/3'.
'Ie/3GO/3EO/3AO/vSu/vSufvOefvMefvIefvGOfvEOfvCOfvAO'.
'fnUufnSufnMd7nId7nGN7nGNbnEN7nCN7nAN7ejN7ejNbec97e'.
'c9beUtbeQtbeIdbeGNbeENbeCNbeANbWpdbWa9bWQs7WGM7WEM'.
'7WCM7WAM7Otc7Orc7OnM7OSsbOIb3OGMbOEMbOCMbOAM7OAMbG'.
'pcbGnMbGe8bGa8bGKbXGEL3GCL3GAL3FucXBu73AvsC/v7+9pb'.
'29Ka29GLW9ELW9CLW9AL29ALW5rrm1lLW1e7W1MbW1GKW1EK21'.
'CLW1CK21AK2tjK2thKWtMaWtIaWtGJytCK2tCKWtAK2tAKWlhK'.
'Wle6WlEJylCJylAKWlAJyca5ycGJScEJScCJScAJycAJSUWpSU'.
'UoyUKZSUEIyUCIyUAJSUAIyMUoyMSoyMIYSMEISMCISMAIyMAI'.
'SECHuEAISEAHt7MXt7EHt7CHt7AHt7AHNzKXNzEGtzAHNzAGtr'.
'GGtrEGNrCGtrAGtrAGNjCFpjAGNjAFpaAFpaAFIpZn4bAAAAAX'.
'RSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsS'.
'AdLdfvwAAAAHdElNRQfTAwkRFB0ymoOwAAAB9UlEQVR4nGNgIA'.
'K42hhqGtm5+WFIWClKycvLK6gbuARGoEj4aMjLSElISUir6Tt7'.
'x+aEIWR8leQlwEBSTc/CK7awLguuR0lGQkJMVFRUTFJVzwko1d'.
'oFk9OQl5IQE+Dh5hVR0TV3CkkvbJgyASJjDZIR5GBl5eRX0TH1'.
'DEqrbJ2ypBEspSgvJSXKw8bMxMavbOLoGZNf1TZlybw4oIyfLN'.
'BxotxsLEzsQiaOHkFpBQ2905esrAZK2SpIAaUEuDm5+LTNPAKj'.
'C+pbps1evrIDKGWnLictKSkuLKyoZQyUya9o7Z2+YMXKGUApew'.
'M9PTVdXR0TEwf3wOjUirruafOXL18xFyjl72Kpb25qaurg4REU'.
'EFVe2zJ5zpLlK1aCpbydnZ2dnDwDA6NTopLLeiZNXbB8BcTAyP'.
'TQ0JDg4KCY1NS83JKmiVOBepYvX9UPlAovzEiPSU/LLyior2vq'.
'mjZr3vLlIF01IC+XVhUWFlZW1Lc290ycOGfxohVATSsXx4Oksn'.
'vaWlsb2tq6J0+bM2/RohVA81asbIcEYueU3t7JU6ZNnwNyGkhm'.
'+cp5CRCppJnzZ8+ZM3/JUogECBbBIixr8Yqly8FCy8F6ltUgoj'.
'lz7sqVK2ByK+cVMSCDxoUrwWDVysXt8WhJKqG4Y8bcuTP6qrGk'.
'QwwAABiMu7T4HMi4AAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bl_gray.png
//==========================================================
$this->imgdata_large[5][0]= 905 ;
$this->imgdata_large[5][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAABO1'.
'BMVEX////////3///39/fv7+/e5+fW3t7Wzs7WxsbG1tbGzsbG'.
'xsbDxMS/v7++wMC+v7+9zsa9xsa9vb29tbW9ra29pa24uLi1xs'.
'a1vb21tbWxtrattbWmpqalra2cra2cpaWcnJycjIyUpaWUnJyU'.
'lJSUjIyMnJyMnJSMlJSMlIyMjJSMjIyElJSElIyEjIyEhIR7jI'.
'x7hIR7hHt7e3t7e3N7e2tzhIRze3tze3Nzc3Nre3trc3Nrc2tr'.
'a2tjc3Njc2tja3Nja2tjY2NjWlpaa2taY2taY2NaY1paWlpaUl'.
'JSY2NSY1pSWlpSWlJSUlJSUkpKWlpKWlJKUlpKUlJKUkpKSkpK'.
'SkJCUlJCUkJCSkpCSkJCQkI5Sko5QkI5Qjk5OUI5OTkxQkIxOT'.
'kxMTkxMTEpMTEhMTEhKSkYISEpy7AFAAAAAXRSTlMAQObYZgAA'.
'AAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdE'.
'lNRQfTAwkRFQfW40uLAAABx0lEQVR4nI3SbXfSMBQA4NV3nce5'.
'TecAHUywRMHSgFuBCFsQUqwBS1OsWQh0GTj//y8wZUzdwQ/efM'.
'tzcm/uuXdj4z9ic/PR9k4qk1qDnf0X2/uZzKt8GaRvSubg4LVp'.
'mkWzCGAT/i3Zsm2XNQHLsm2n2937LaaNnGoJFAEo27B50qN0ay'.
'Wg26lXsw8fP8nmzcJb2CbsnF5JmmCE8ncN404KvLfsYwd7/MdV'.
'Pdgl/VbKMIzbuwVgVZw2JlSKJTVJ3609vWUY957lgAUd1KNcqr'.
'yWnOcOPn8q7d5/8PywAqsOOiVDrn42NFk+HQ7dVuXNYeFdBTpN'.
'nY5JdZl8xI5Y+HXYaTVqEDp1hAnRohZM03EUjMdhn5wghOoNnD'.
'wSK7KiiDPqEtz+iD4ctdyAifNYzUnScBSxwPd6GLfRURW7Ay5i'.
'pS5bmrY8348C5vvUI+TLiIVSJrVA0heK/GDkJxYMRoyfCSmk4s'.
'uWc3yic/oBo4yF374LGQs5Xw0GyQljI8bYmEsxVUoKxa6HMpAT'.
'vgyhU2mR8uU1pXmsa8ezqb6U4mwWF/5MeY8uLtQ0nmmQ8UWYvb'.
'EcJaYWar7QhztrO5Wr4Q4hDbAG/4hfTAF2iCiWrCEAAAAASUVO'.
'RK5CYII=' ;
 
//==========================================================
// File: bl_brown.png
//==========================================================
$this->imgdata_large[6][0]= 1053 ;
$this->imgdata_large[6][1]=
'iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABoV'.
'BMVEX////Gzs7GvbXGrZTGpXu9nHO1nHO1nIy9taXGxs7GtaXO'.
'nHPGlFrGjEq9hEq1hEqte0Klczmcazmce1KtnIzGxsbGvb3OlF'.
'LOlFq9hFKte0qcc0KUYzGEWimMc1K9ta3OnGvOnGPWnGO9jFq9'.
'jFKlc0KUazmMYzl7UilzUjGtpZzGxr3GnGPWpWvepXO1hFJ7Wj'.
'FrSiFjUjG1ra3GnHPvxpT/5733zpythFKUa0KEYzlzUilaOSF7'.
'Wjm9jErvvYz/99b///f/78bnrYS1hFqle0p7UjFrSiljQiFCMR'.
'iMhHO9lGvGjFLWnGv/3q3////erXuthEqlc0paQiFKMRhSQin/'.
'1qX/997//++cc0pjSilaQilKORhCKRiclIy9pYzGlGPntYT33q'.
'3vvZSEWjlSOSE5KRB7c2O1lHutczmthFqte1JrWkqtjGtCKRBa'.
'SjmljGuca0KMYzGMaznOztaclISUYzmEWjFKOSF7a1qEYzFaSi'.
'GUjISEa0pKOSm9vb2llIxaQhg5IQiEc2tzY0paORilnJy1raVS'.
'OSljUkJjWkKTpvQWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
'gAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkREiei'.
'zP2EAAAB9UlEQVR4nGWS/VfSUBjHL5QluhhBxtwyWcCus5Blpm'.
'wDC4ONaWXCyBi7RMZmpQ2Bypm9W/byV3cHHo/W88s95/s5z/d5'.
'uwCcCh/4L3zAf+bs0NC588On9QAYGSUuBINk6GI4cmnsBLk8Go'.
'1SFEGMkzRzZeLq5JE8FvDHouw1lqXiCZJOcnCKnx4AcP0GBqmZ'.
'mRgRT9MMB4Wbs7cGSXNRik3dnp9fiMUzNCNKgpzN9bsaWaQo9s'.
'7dfH7pXiFTZCBU1JK27LmtBO8TDx7mV1eXHqXXyiIUFLWiVzHx'.
'BxcJIvV4/cn6wkqmWOOwmVE3UQOAp6HxRKL5bGPj+VwhUhalFq'.
'8alm5vAt+LlySZTsebzcKrraIIW4JqZC3N3ga+1+EQTZKZta1M'.
'pCZCSeDViqVrThsEdsLJZLJYLpZrHVGScrKBvTQNtQHY6XIM02'.
'E6Ik7odRW1Dzy3N28n3kGuB3tQagm7UMBFXI/sATAs7L5vdbEs'.
'8Lycm923NB0j5wMe6KOsKIIyxcuqauxbrmlqyEWfPmPy5assY1'.
'U1SvWKZWom9nK/HfQ3+v2HYZSMStayTNN0PYKqg11P1nWsWq7u'.
'4gJeY8g9PLrddNXRdW8Iryv86I3ja/9s26gvukhDdvUQnIjlKr'.
'IdZCNH+3Xw779qbG63f//ZOzb6C4+ofdbzERrSAAAAAElFTkSu'.
'QmCC' ;
 
//==========================================================
// File: bl_darkgreen.png
//==========================================================
$this->imgdata_large[7][0]= 1113 ;
$this->imgdata_large[7][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAB2l'.
'BMVEX////////3///v///n/+/e99bW/+/W99bO786/v7++vr69'.
'/96999a7wb24vbu1/9a1zqW1u7itxrWosq6l772l1qWlxrWlxq'.
'2lva2cxpSU562U3q2UxqWUvaWUpZyM77WM57WMvYyMtZyMrZyM'.
'pZSMnJSEvZyEtYyErZSElIx7zpR7xpx7xpR7vZR7jIRz1pRzxp'.
'RzjIRrzpRrzoxrxoxrtYRrrYxrrXtrpYRrhHNjzoxjxoxjxoRj'.
'vYRjtYRjrXtjpXtjlGNje2tazoxazoRaxoxaxoRavYRatYRatX'.
'tarXtapXNanHNajFpae2tSzoRSxoRSvXtStXtSrXtSrXNSpXNS'.
'nHNSnGtSlGtSlGNSjGtSjGNKvXtKtXNKrXNKpWtKnGtKlGNKjG'.
'NKhGNKhFJKc1pKa1JCrWtCpWtCnGtClGNCjGNCjFpChFpCe1JC'.
'a1JCY1I5pWs5nGM5lGM5jFo5hFo5e1o5c0o5WkoxjFoxhFoxhF'.
'Ixe1Ixc1Ixc0oxa0ophFIpe0opc0opa0opa0IpY0IpWkIpWjkp'.
'UkIpUjkhc0oha0IhY0IhWjkhWjEhUjkhUjEhSjEhSikhQjEhQi'.
'kYWjkYSjEYSikYQjEYQikQSikQQikQQiEQOSExf8saAAAAAXRS'.
'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
'LdfvwAAAAHdElNRQfTAwkRFCaDkWqUAAAB+ElEQVR4nI3S+1vS'.
'UBgHcGZlPV0ks/vFrmQWFimJjiwiYUJWjFBWFhClyZCy5hLrwA'.
'x2EIwJC1w7zf2vnU0re+iHvs9++7x7zznvORbLf+TA6ct9fYMX'.
'jrfAUYefpp+/iM1ykxf/lmuhUZ/PTwXC8dml5Wcd23o5H5Mk6b'.
'5NUU8icXbhS67rNzn9JDnguOEYGQtEEtwC+Crs3RJ76P5A/znr'.
'vsNX7wQnEiwHCtK7TTkW8rvdZ9uJtvZTLkxpHhSrP66bNEj7/P'.
'3WNoLYeeSWQQCIpe9lQw7RNEU5rDsIYtcJ14Nocg7kRUlBNkxn'.
'YmGKcp7cv3vPwR7XOJPmc0VYU3Sv0e9NOBAYG7Hbz/cMjTMveZ'.
'CHkqxuTBv0PhYJB4N3XR6PJ5rMAPMnpGUxDX1IxSeMTEaZp1OZ'.
'nGAIQiYtsalUIhFlmGTy3sO3AizJCKn6DKYryxzHsWyaneMzr6'.
'cWxRVZVlFTe4SpE3zm+U/4+whyiwJcWVMQNr3XONirVWAklxcE'.
'EdbqchPhjhVzGpeqhUKhWBQhLElr9fo3pDaQPrw5xOl1CGG1JE'.
'k1uYEBIVkrb02+o6RItfq6rBhbw/tuINT96766KhuqYpY3UFPF'.
'BbY/19yZ1XF1U0UNBa9T7rZsz80K0jWk6bpWGW55UzbvTHZ+3t'.
'vbAv/IT+K1uCmhIrKJAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bl_green.png
//==========================================================
$this->imgdata_large[8][0]= 1484 ;
$this->imgdata_large[8][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMMFjM4kcoDJQAABVlJREFUeNq9ll2MJFUVx3/11V'.
'Vd/TE9vU0v4zLDwJIF16jBqLAPhsRXEiDqg0QTJiQSjcSNvCzw'.
'sBEDDxizhvAAxBgf1oR9QF9NiE9ESFZkQyZB5WtddmdnZ3qqqr'.
'uqbt367Cofqu3ZZpWVaDzJfbkf53//55z/PVdZXV3l/2H6f7Lp'.
'5VdOV/4Nb+GmHpUeA7AdBNxc3kafNb73jRPK9Xwon8ToxVefqU'.
'b91wibH5EkCQBCizFihTSviHUHR0hWws9xe3wvJ7/7nPKpgX5y'.
'9oFqt3eOgWniRBoAbUBGGqZUibSYaeoT2B5bnkdaSA6793Cv/S'.
'QPPbihXBfo5VdOV+8dfgnvwAU62YH5fCZ12sDujFkwyegCqTrB'.
'iUOKTOJKj8jr88jS8zy6cXwBTP048nuHX0I0nDlIp7RpTG7kM0'.
'sdyAYsTVukUuWGhlWHMq0ITL92lnUp9R1Obz/GmTNnqn9bDD8/'.
'+0D1oX0O0zQZZDYCsK2j3Gl9jQqDfHiei8GfiKVLlsZkJaBAN1'.
'0i6PgwUbB0GxG5/PrtE/xLRr959Znqw9452oVNI+jiJhnr1pe4'.
'k29zB1/nFr5Kj7tpt1YYhJ0FJ7nUYbcJQBgahN2MzeCP/OipR6'.
'prgN6Qr6ELFQFUWoRpNVjlKwxZB8DCpE+PtfEKqV1cUzxpVudu'.
'GTBHA5Y1g99e+dUio9O/P1Vpq+/WE5GGjDSMoAtAQjrf3C52IP'.
'QxpY4WK2hpReka9Gfrhqgz0bACRoCWjDh56kQ1z9FeuUUQxVhK'.
'B92sD1VahM+bAJgcoJhGjP/6Ln8rAgDiRCVRKiIzxMkkodBJ85'.
'im1IlEHbE4k1xyNveL4YP8HarmGJIOpqyjeQmfNHmTvnqZTWBt'.
'vIJXpPwlukJSuSTKGK3pEwtJmiX00ZlInTyNscImO6XBITvH1c'.
'8vVt2OucdKvIyeKRTNCivsEMgcpg6taYs30nfq0Gqg6hOSSFJ4'.
'BSnJPht0IqEjWmOGocEI6F0J94F0qaL6BntTF0MtUfweKQKAPU'.
'Wwp4OcVnQAmVb0p9DLOzjEhEKnGRmoRc7EzRGlwA6NujAKG4yP'.
'6Sjwc4aVznZ7DK0xXdkDoJf0kGmFBniFBOBGcZSCCSKd0IwN0k'.
'IS+QZWCGVZex4BnUxya3+Zt9iugQbcRFpIAtuHvAZulPUdLhUJ'.
'RqegI3WcqaSXddlT3idsWMSRRGkEtNwmyTifAwyBo7LP+11J0e'.
'7tM7pZOYblHkBLcqZ5LcYtw6Wbd4CM3SpE9foYZsIHoqDKCrbz'.
'mLSQtPwmuhXgtBLs0GBdbXOhFGB7WBKO2F8GXt9/VO97Ya3atF'.
'7nUHnwGjGGQqcPxFEdFqURkEidiZszAERoYIsGju1hq21kWee3'.
'bw15+8WpsvAy3K1+i3JkkhZyPpxxjjPOsfOYiZ+TFhLPzQnHOU'.
'tpzGB2dgA4tscIkKIx19Cxg/fPL7vQJu47eXt1VvsDK8pwPueZ'.
'PuZoQMOqhRoJHSs0kKLBWjvjYinmeQGw1TaX1RFdfZ3LMzYLjA'.
'C++dkn6AaH2Nobk6cxEzdnuG0TdC8zvdJkN0hqkFkO/jwL0fxa'.
'so8sBcuFzQ+/+MRC+BeAHnpwQzn++ee5KT9Eshuy46dcKAXm32'.
'0uzPQhS4GttkH2GQID2Wc0Y4LtAbDxhZ/x5A+e/uTG9+jGceXH'.
'9/ySnnIXnUzOxXe1038mW3ZynNmam4yYWkO+f9cv+Oljz16/lV'.
'9tDz/9nerc1hm8ZEScSRK7VvtYl1i1dklsOKyvc+zg/bzw1O8+'.
'/efkajt56kR1ydlEJBc5H46xzbrJ3dY9wrB7hGcff+6/+279L+'.
'0fHxyiE8XMLl4AAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bl_blue.png
//==========================================================
$this->imgdata_large[9][0]= 1169 ;
$this->imgdata_large[9][1]=
'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAACEF'.
'BMVEX/////////7//35//v1v/exv/Wvf/Wrf/Wpf/Orf+/v7+9'.
'tc69jP+9hP+5ucW1tc6tlP+rq7Wlpdalpcalpb2cnM6cnMacc/'.
'+cWv+UlLWUjN6UjK2Uc/+Ma/+MUv+EhKWEa/+EQvd7e8Z7e7V7'.
'e6V7c957Wv9za9Zza8ZzSv9ra5xrSv9rOf9rMe9jUudjQv9jOe'.
'9aWpRaUt5aUpRaSu9aSudSUoxSSs5SSoxSMf9KQtZKOfdKMedK'.
'Kf9KKe9CKf9CKb1CKa1CIfdCIedCId45MXs5Kfc5If85Iec5Id'.
'Y5GP8xMbUxMXsxKc4xKZQxIf8xGP8xGO8xGN4xGNYxGL0xGK0p'.
'KXMpIYwpGP8pGO8pGOcpGNYpGM4pEP8pEPcpEOcpEN4pENYpEM'.
'YpEL0hGKUhEP8hEPchEO8hEOchEN4hENYhEM4hEMYhELUhCP8h'.
'CO8hCN4YGJwYGGsYEL0YEK0YEHMYCN4YCM4YCMYYCL0YCKUYAP'.
'8QEJQQEIwQEHsQEGsQCM4QCLUQCK0QCKUQCJwQCJQQCIwQCHMQ'.
'CGsQAP8QAPcQAO8QAOcQAN4QANYQAM4QAMYQAL0QALUQAKUQAJ'.
'QQAIQICGsICGMIAO8IANYIAL0IALUIAK0IAKUIAJwIAJQIAIwI'.
'AIQIAHsIAHMIAGsIAGMAAN4AAMYAAK0AAJQAAIwAAIQAAHMAAG'.
'sAAGMAAFrR1dDlAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkRFRPMOZ'.
'/2AAAB+klEQVR4nGNgIAIIqeqZmBqpi2JISNml5lVXV3d198Yo'.
'oUjwm1SnxsbGRsSm5ZfNXO4tjCTjVh0ABhFx6QV9E1Y0S8JkuN'.
'3yAgLc7W3t/QPi4jPKJ8ye1yoIlTKpjvVy15eVUbN0i4zKLJ8w'.
'ae6qcKgLqmMj3PUFWFl5NJ0CExLLJzbNW7BWCyxlXR0ba6/Axs'.
'zELmfnkRBT0QiSKgXJCOflxUbYy3KyMHEoOrtEZ1c2TZ6/cMl6'.
'eaCUamdsbIC7tjgPr4SBS3BMMVDTwkXr1hsDpYy6UmMj/O0tdX'.
'QNbDxjknJLWqYsXLx0vStQynxGflpkZGCgs7Onp29SbtNkoMy6'.
'pevCgFJWy3oyMuKjgoKCPWNCvEuqWhcsWrJ06XqQlPnMvrKyrM'.
'TomJjkZAfHlNa2qdOWrlu63gcopbG8v7+hvLwip7g4JdSxsLZu'.
'8dKlS9ettwBKic2eNXHChIkTG5tKqgpr2uo6loLAehWQx0LnzJ'.
'49p6mpeXLLlNq6RUvqly6dvnR9Bx9ISnnlvLmT582bMr9t4aL2'.
'+vrp60GaDCGB6Ld6wfwFCxYCJZYsXQ+SmL6+FBryInVrFi1atH'.
'jJkqVQsH6pNCzCJNvXrQW6CmQJREYFEc2CYevXrwMLAyXXl0oz'.
'IAOt0vVQUGSIkabkDV3DwlzNVDAksAAAfUbNQRCwr88AAAAASU'.
'VORK5CYII=' ;
 
//==========================================================
// File: bs_red.png
//==========================================================
$this->imgdata_small[0][0]= 437 ;
$this->imgdata_small[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAk1'.
'BMVEX////////GxsbGra3/xsbOhITWhIT/hIT/e3v/c3P/a2vG'.
'UlK1SkrOUlL/Y2PWUlLGSkrnUlLeSkrnSkr/SkqEGBj/KSmlGB'.
'jeGBjvGBj3GBj/EBD/CAj/AAD3AADvAADnAADeAADWAADOAADG'.
'AAC9AAC1AACtAAClAACcAACUAACMAACEAAB7AABzAABrAABjAA'.
'BuukXBAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGDNEMgOYAAAAm0'.
'lEQVR4nI3Q3RKCIBAFYGZMy9RKzX7MVUAUlQTe/+kS0K49d3wD'.
'7JlFaG+CvIR3FvzPXgpLatxevVVS+Jzv0BDGk/UJwOkQ1ph2g/'.
'Ct5ACX4wNT1o/zzUoJUFUGBiGfVnDTYGJgmrWy8iKEtp0Bpd2d'.
'jLGu56MB7f4JOOfDJAwoNwslk/jOUi+Jts6RVNrC1hkhPy50Ef'.
'u79/ADQMQSGQ8bBywAAAAASUVORK5CYII=' ;
 
 
//==========================================================
// File: bs_lightblue.png
//==========================================================
$this->imgdata_small[1][0]= 657 ;
$this->imgdata_small[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABVl'.
'BMVEX////////d///AwMC7wcS08P+y+P+xxdCwxM+uws2twMur'.
'vsinzNynytylzuKhyN6e5v6d5P+d1fOcwNWcu8ub4f+at8iZ3v'.
'+ZvdGY2/yW2f+VscGU1vuT1fqTr72Sx+SSxeKR0fWRz/GPz/OP'.
'rr+OyeqMy+6Myu2LyeyKxueJudSGw+SGorGDvt+Cvd6CvN2Aud'.
'p+uNd+t9Z9tdV8tdR8tNN6sc94r813rct2q8h0qcZ0qMVzp8Rx'.
'o8Bwor5tn7ptnrptnrlsnbhqmbRpmbNpi51ol7Flkqtkkqtkka'.
'pjj6hijaRhjaZgi6NfiqJfiaFdh55bhJtag5pZgphYgJZYf5VX'.
'cn9Ve5FSeI1RdopRdYlQdYlPc4dPcoZPcoVNcINLboBLbH9GZn'.
'hGZXdFZHZEY3RDYnJCXW4/W2s/WWg+Wmo7VmU7VGM7U2E6VGM6'.
'VGI5UV82T1wGxheQAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
'gAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGTok'.
'9Yp9AAAAtElEQVR4nGNgIBaw8wkpKghzwvksPAKiUsraprYiLF'.
'ARXkE2JiZ1PXMHXzGIAIekOFBE08TGLTCOCyzCLyvDxsZqZOnk'.
'E56kAhaRV9NQUjW2tPcMjs9wBYsY6Oobmlk7egRGpxZmgkW0zC'.
'2s7Jy9giKT8gohaiQcnVzc/UNjkrMLCyHmcHr7BYREJKTlFxbm'.
'QOxiEIuKTUzJKgQCaZibpdOzQfwCOZibGRi4dcJyw3S4iQ4HAL'.
'qvIlIAMH7YAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bs_gray.png
//==========================================================
$this->imgdata_small[2][0]= 550 ;
$this->imgdata_small[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAMAAADH72RtAAABI1'.
'BMVEX///8AAAD8EAD8IAD8NAD8RAD8VAAYGBi/v7+goKCCgoJk'.
'ZGRGRkb8yAD83AD87AD8/AD4+ADo+ADY+ADI+AC0+ACk+ACU+A'.
'CE+AB0/ABk/ABU/ABE/AAw/AAg/AAQ/AAA/AAA+AAA6BAA2CAA'.
'yDQAtEQApFQAlGQAhHQAdIgAZJgAVKgARLgAMMgAINwAEOwAAP'.
'wAAPgIAPAQAOgYAOAkANgsANA0AMg8AMBEALhMALBUAKhcAKBo'.
'AJhwAJB4AIiAAID////4+Pjy8vLs7Ozm5ubg4ODa2trT09PNzc'.
'3Hx8fBwcG7u7u1tbWurq6oqKiioqKcnJyWlpaQkJCJiYmDg4N9'.
'fX13d3dxcXFra2tkZGReXl5YWFhSUlJMTExGRkZAQEA1BLn4AA'.
'AAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIA'.
'AAsSAdLdfvwAAAAHdElNRQfTAwkUGiIctEHoAAAAfElEQVR4nI'.
'2N2xKDIAwF+bZ2kAa8cNFosBD//yvKWGh9dN+yk9kjxH28R7ze'.
'wzBOYSX6CaNB927Z9qZ66KTSNmBM7UU9Hx2c5qjmJaWCaV5j4t'.
'o1ANr40sn5a+x4biElrqHgrXMeac/c1nEpFHG0LSFoo/jO/BeF'.
'lJnFbT58ayUf0BpA8wAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bs_greenblue.png
//==========================================================
$this->imgdata_small[3][0]= 503 ;
$this->imgdata_small[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAxl'.
'BMVEX///////+/v79znJQhSkJ7raU5hHtjraVKnJRCjIRClIyU'.
'9++E595avbVaxr2/v7+ctbWcvb17nJxrjIx7paUxQkK9//+Mvb'.
'17ra2Evb17tbVCY2MQGBiU5+ec9/eM5+d71tZanJxjra1rvb1j'.
'tbVSnJxara1rzs5jxsZKlJRChIQpUlIhQkJatbVSpaU5c3MxY2'.
'MYMTEQISFavb1Sra1KnJxCjIw5e3sxa2spWlpClJQhSkoYOTkp'.
'Y2MhUlIQKSkIGBgQMTH+e30mAAAAAXRSTlMAQObYZgAAAAFiS0'.
'dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfT'.
'AwkUGTIqLgJPAAAAqklEQVR4nI2QVxOCMBCEM6Mi2OiCvSslJB'.
'CUoqjn//9TYgCfubf9Zu9uZxFqO+rscO7b6l/LljMZX29J2pNr'.
'YjmX4ZaIEs2NeiWO19NNacl8rHAyD4LR6jjw6PMRdTjZE0JOiU'.
'dDv2ALTlzRvSdCCfAHGCc7yRPSrAQRQOWxKc3C/IUjBlDdUcM8'.
'97vFGwBY9QsZGBc/A4DWZNbeXIPWZEZI0c2lqSute/gCO9MXGY'.
'4/IOkAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bs_yellow.png
//==========================================================
$this->imgdata_small[4][0]= 507 ;
$this->imgdata_small[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAzF'.
'BMVEX///////+/v79zYwCMewDOxoTWzoTezkr/5wj/5wDnzgDe'.
'xgC1pQCtnACllACcjACUhABjWgDGvVK1rUrOxlLGvUqEexilnB'.
'jv3hj35xj/7wj/7wD35wDv3gDn1gDezgDWxgDOvQDGtQC9rQCE'.
'ewB7cwBzawBrYwDWzlLn3lLe1krn3kre1hi9tQC1rQCtpQClnA'.
'CclACUjACMhAD/9wC/v7///8bOzoT//4T//3v//3P//2v//2Pn'.
'50r//0r//yn39xj//xD//wBjYwDO8noaAAAAAXRSTlMAQObYZg'.
'AAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAH'.
'dElNRQfTAwkUGSDZl3MHAAAAqElEQVR4nI3QWRNDMBAA4My09E'.
'IF1SME0VT1okXvM/3//6kEfbZv+81eswA0DfHxRpOV+M+zkDGG'.
'rL63zCoJ2ef2RLZDIqNqYexyvFrY9ePkxGWdpvfzC7tEGtIRly'.
'nqzboFKMlizAXbNnZyiFUKAy4bZ+B6W0lRaQDLmg4h/k7eFwDL'.
'OWIky8qhXUBQ7gKGmsxpC+ah1TdriwByqG8GQNDNr6kLjf/wAx'.
'KgEq+FpPbfAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bs_darkgray.png
//==========================================================
$this->imgdata_small[5][0]= 611 ;
$this->imgdata_small[5][1]=
'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAABJl'.
'BMVEX////////o8v/f6O7W4OnR3PXL1OTL0evEyLvCzePAwMC/'.
'v7a8wsq7t7C1xum1vtS1q6GzopmyxeKsrsOqvNWoq7anvN+nsb'.
'qhrcGgqbGfpq6cp7+bqMuVmJKRm7yPlKKMnL6FkKWFipOEkLSE'.
'j6qEhoqAiaB+jqd8haF7hZR4iJt4g5l3hZl2gIt2cod1hJVzeY'.
'VzboJvhp9sfJJsb41peY1pd5xpdoVod4xndI5lcHxka4BjcYVg'.
'Z3BfboFbb4lbZnZbYntaZ4laZYVZV3JYYWpXX3JWWm5VX4RVW2'.
'NUYX9SXHxPWn5OVFxNWWtNVXVMVWFKV3xHUGZGU3dGTldFSlxE'.
'Sk9ESXBCRlNBS3k/SGs/RU4+R1k9R2U6RFU2PUg0PEQxNU0ECL'.
'QWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAA'.
'CxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGQmbJetrAAAAtklEQV'.
'R4nGNgwAK4JZTNNOWlYDxhMT4ZDTOzQE1uMF9CiJWVU0LbxDlS'.
'G8QVF+FnZ2KRNHAIiPUHaZGSlmZj5lH19A1KjLUA8lXU5MWllF'.
'yjo30TYr2BfG19G11b37CEeN84H38gX1HbwTUkOjo+zjfG3hLI'.
'l1exCvCNCwnxjfMz0gTyRdXNHXx9fUNCQu2MwU6SN3ZwD42LCH'.
'W30IK4T8vUJSAkNMhDiwPqYiktXWN9JZj7UQAAjWEfhlG+kScA'.
'AAAASUVORK5CYII=' ;
 
 
//==========================================================
// File: bs_darkgreen.png
//==========================================================
$this->imgdata_small[6][0]= 666 ;
$this->imgdata_small[6][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABX1'.
'BMVEX////////l/+nAwMC86r+8wb28wby8wLy78sCzw7SywrSx'.
'wLKwvrGuvK+syK+ryq2rx62n36ym3aumxKmk2qij0Keh16ahva'.
'Og1aSguKKe06KeuaCetZ+d0KGdtZ+bz6Cay56ZyZ2Zwp2Zr5qZ'.
'rpqYwJuXyZuXrJmVw5mUxZiTxJeTw5eTq5WRwJWPtJKOvZKKuI'.
'6Kt42Kn4yJt42ItIuGsomFsYmEsIiEr4eDr4eBrIR/qoN+qIJ8'.
'poB7pH56o356on14nnt2nXl0mndzmnZzmXZymHVwlXNvlHJukn'.
'FtiHBqjm1qjW1oi2toiWpniWplh2hlhmdkhWdig2VggGNgf2Je'.
'fmFdfGBde19bbl1aeFxXdFpWclhVclhVcVdUcFZTb1VSbVRQal'.
'JPaVFKY0xKYkxJYUtIYEpHX0lEWkZCWERCV0NCVkM/U0A+U0A+'.
'UUA+UEA9Uj89UT48Tj45TDvewfrHAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
'RQfTAwkUGRjxlcuZAAAAtElEQVR4nGNgIBZw8osqqIpzw/msfI'.
'IiUmr6lo6SbFARASEOJiYtQ2uXADmIAJeEGFBE18LBMySBBywi'.
'LC/LwcFiZuvmH5WiAxZR0tRW1DC3dfYJS8zyAouYGBibWtm7+o'.
'TEpZfkgEX0rG3snNx9Q2NSCksgaqRd3Ty8gyLiU/NKSiDmcPsF'.
'BodHJ2UUlZTkQ+xikIlNSE7LLgECZagL2VQyc0H8YnV2uD94jS'.
'ILIo14iQ4HALarJBNwbJVNAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bs_purple.png
//==========================================================
$this->imgdata_small[7][0]= 447 ;
$this->imgdata_small[7][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAnF'.
'BMVEX///////+/v7/Gvca9rb3Grcb/xv+1hLWte629hL21e7XG'.
'hMbWhNbOe87We9b/hP//e/97OXv/c///a///Y/+cOZz/Sv/WOd'.
'bnOefvOe//Kf9jCGNrCGv/EP//CP/nCOf/AP/3APfvAO/nAOfe'.
'AN7WANbOAM7GAMa9AL21ALWtAK2lAKWcAJyUAJSMAIyEAIR7AH'.
'tzAHNrAGtjAGPP1sZnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGS'.
'o5QpoZAAAAnElEQVR4nI3Q2xJDMBAG4MyQokWrZz3oSkJISJH3'.
'f7dK0Gv/Xb7J7vyzCK0NjtPsHuH/2wlhTE7LnTNLCO/TFQjjIp'.
'hHAA6bY06LSqppMAY47x+04HXTba2kAFlmQKr+YuVDCGUG2k6/'.
'rNwYK8rKwKCnPxHnVS0aA3rag4UQslUGhrlk0Kpv1+sx3tLZ6w'.
'dtYemMkOsnz8R3V9/hB87DEu2Wos5+AAAAAElFTkSuQmCC' ;
 
 
//==========================================================
// File: bs_brown.png
//==========================================================
$this->imgdata_small[8][0]= 677 ;
$this->imgdata_small[8][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABaF'.
'BMVEX//////////8X/3oD/3nj/1HX/0Gr/xGP/rkv/gBf+iS/2'.
'bAL1agDxaQDuZwDrZwLpZQDmZQLlZADjcx7gZATeYQDdZgraXw'.
'DZXwHYXgDXiEvXZAvUjlfUXwXTjVfTbR7ShUvRbR7RWwDMWQDL'.
'WADKooLKWADJoYLJgkvHWATGoILFn4LFgEvFVgDEZx7EVQDDt6'.
'/DVQDCt6/CnoLChlfCVADAwMC+hFe+UgC8UgC6UQC4gVe4UAC3'.
'gVe3UAC1gFe1eUu1TwC1TgCzTgCwTQKuTACrSgCqSgCpSgCpSQ'.
'CodEulSACkRwCiRgCdRACcRACaQwCYQgCWQgKVQQCVQACUQACS'.
'UR6RPwCOPgCNPQCLPACKPACJOwCEOQCBOAB+NwB9NgB8NgB7NQ'.
'B6NwJ4NAB3RR52MwB0MgBuLwBtLwBsLwBqLgBpLQBkLQJiKgBh'.
'KgBgKwRcKABbKQJbJwBaKQRaJwBYKAJVJQDZvdIYAAAAAXRSTl'.
'MAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLd'.
'fvwAAAAHdElNRQfTAwkUGho0tvl2AAAAtklEQVR4nGNgIBaoSg'.
'mLKGpowfkGMty8AqJKpi4mRlAROR5ONg4JFUv3YHOIgDo/HwsT'.
'q6yps29EsjZYREFIkJ2ZS9/OMzA20wEsIi8uKSZtaOPmH5WSFw'.
'YW0VRW07Vw8vCLSMguLwCL6FlaObp6B0TGZxSXQ9TouHv6+IXG'.
'JGYWlpdDzNEKCgmPjkvLKS0vL4LYxWAen5SelV8OBNZQFxrZ5h'.
'aC+GX2MDczMBh7pZakehkTHQ4AA0Am/jsB5gkAAAAASUVORK5C'.
'YII=' ;
 
//==========================================================
// File: bs_blue.png
//==========================================================
$this->imgdata_small[9][0]= 436 ;
$this->imgdata_small[9][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAk1'.
'BMVEX///////+/v7+trcbGxv+EhM6EhNaEhP97e/9zc/9ra/9S'.
'UsZKSrVSUs5jY/9SUtZKSsZSUudKSt5KSudKSv8YGIQpKf8YGK'.
'UYGN4YGO8YGPcQEP8ICP8AAP8AAPcAAO8AAOcAAN4AANYAAM4A'.
'AMYAAL0AALUAAK0AAKUAAJwAAJQAAIwAAIQAAHsAAHMAAGsAAG'.
'ONFkFbAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGhNNakHSAAAAmk'.
'lEQVR4nI3P2xKCIBAGYGfM6SBWo1nauIqogaDA+z9dK9Lhrv47'.
'vtl/2A2CfxNlJRRp9IETYGraJeEb7ocLNKznia8A7Db7umWDUG'.
'sxAzhurxRHxok4KQGqCuEhlL45oU1D2w5BztY4KRhj/bCAsetM'.
'2uObjwvY8/oX50JItYDxSyZSTrO2mNhvGMbaWAevnbFIcpuTr7'.
't+5AkyfBIKSJHdSQAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bs_green.png
//==========================================================
$this->imgdata_small[10][0]= 452 ;
$this->imgdata_small[10][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAn1'.
'BMVEX///////+/v7+/v7/G/8aUxpSMvYyUzpSMzoyM1oxarVqE'.
'/4R7/3tavVpKnEpaxlpz/3Nr/2tKtUpj/2Na51pKzkpK1kpK50'.
'pK/0oYcxgp/ykYlBgY3hgY7xgY9xgQ/xAI/wgA/wAA9wAA7wAA'.
'5wAA3gAA1gAAzgAAxgAAvQAAtQAArQAApQAAnAAAlAAAjAAAhA'.
'AAewAAcwAAawAAYwA0tyxUAAAAAXRSTlMAQObYZgAAAAFiS0dE'.
'AIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAw'.
'kUGgW5vvSDAAAAnklEQVR4nI3QSxKCMAwA0M4gqCgoiiJ+kEAL'.
'LQUq0PufzX7ENdnlJZNkgtDS2CYZvK6bf+7EoKLA9cH5SQzv6A'.
'YloTywsAbYr44FrlgrXCMJwHl3xxVtuuFkJAPIcw2tGB9GcFli'.
'oqEf5GTkSUhVMw2TtD0XSlnDOw3SznE5520vNEi7CwW9+Ayjyq'.
'U/3+yPuq5gvhkhL0xlGnqL//AFf14UIh4mkEkAAAAASUVORK5C'.
'YII=' ;
 
 
//==========================================================
// File: bs_white.png
//==========================================================
$this->imgdata_small[11][0]= 480 ;
$this->imgdata_small[11][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAABm'.
'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
'B3RJTUUH0wMLFTsY/ewvBQAAAW1JREFUeJytkz2u4jAUhT/jic'.
'gfBUKiZhE0bIKeVbCWrIKenp6eDiGlCEEEBArIxvzGU4xeZjLk'.
'jWb05lRXuvbx+exr4bouX1Xjyw7Atz81F4uFBYjjGIDhcCjq1o'.
'k6nN1uZwFerxfP55Msy1itVmRZBsB4PK6YveHkeW5d18XzPIIg'.
'wPd9Wq0WnU6HMAxJkoQoiuynOIfDwUopkVIihKAoCgAcx6Hdbm'.
'OMIU1T5vN55eBKEikljUYDIX6kFUKU9e8aDAZlmjcca+1b7TgO'.
'1+uVy+VS9nzfr8e53++VzdZaiqIgz3OMMWitOZ/PaK0JgqDeRC'.
'mF53lIKYGfr3O73TDGoJQiTVO01nS73XqT4/FIs9kkCAIej0eZ'.
'brPZEMcxSZKgtQZgMpmIWpN+vy+m06n1PK9yTx8Gy+WS/X5Pr9'.
'er9GuHLYoiG4YhSilOpxPr9Zrtdlti/JriU5MPjUYjq7UuEWaz'.
'2d+P/b/qv/zi75oetJcv7QQXAAAAAElFTkSuQmCC' ;
 
 
//==========================================================
// File: bs_cyan.png
//==========================================================
$this->imgdata_small[12][0]= 633 ;
$this->imgdata_small[12][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABPl'.
'BMVEX////////F///AwMCvxsaC1NSC0dGCz8+CzMyA//94//91'.
'//9q//9j//9X4uJX09NXz89Xx8dXxMRL//9L5uZL3d1L2NhLxs'.
'ZLt7cv//8e9fUe8fEe7u4e398epqYehoYX//8L+PgK//8F9fUE'.
'/v4E5+cEb28EZ2cC//8C/v4C/f0CzMwCrq4Cjo4CdXUCaWkCZW'.
'UB/PwA//8A/f0A+/sA8/MA7e0A7OwA6+sA5eUA5OQA4uIA4eEA'.
'3NwA2toA2NgA1dUA09MA0tIA0NAAysoAxsYAxcUAxMQAv78Avr'.
'4AvLwAtrYAtbUAs7MAsLAAra0Aq6sAqKgApaUApKQAoqIAoKAA'.
'n58AmpoAlZUAk5MAkpIAkJAAj48AjIwAiYkAh4cAf38AfX0Ae3'.
'sAenoAcnIAcHAAa2sAaWkAaGgAYmIUPEuTAAAAAXRSTlMAQObY'.
'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
'AHdElNRQfTAwkUGQDi+VPPAAAAtElEQVR4nGNgIBawikipyIiy'.
'wfksfJpGRkamNtr8LFARPiMFHmFDcztXfwGoFi0jLiZuZRtnry'.
'BddrCIiJEGL6eklYO7X3iCOFhE2thESdHawdUnJDZFDiyiamZh'.
'aevk5h0UlZSpBhaRtbN3dPHwDY5MSM+EqBFzc/f0DgiLTkjLzI'.
'SYw6bjHxgaEZeckZmpD7GLQSAqJj4xNRMIBGFuFtRLA/ENhGBu'.
'ZmDgkJBXl5fgIDocAAKcINaFePT4AAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bs_bluegreen.png
//==========================================================
$this->imgdata_small[13][0]= 493 ;
$this->imgdata_small[13][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAvV'.
'BMVEX///////+/v79j//855/8x3v851v9Spb1C1v8AOUqEtcZK'.
'lK1StdYxzv8hxv8AY4QASmNSlK1KpcZKtd4YQlIYnM4YrecIvf'.
'8AtfcAre8AjL0AhLUAc5wAa5QAWnsAQloAKTkAGCFKhJxKrdYY'.
'jL0Ypd4Atf8ArfcApecAnN4AlM4AjMYAe60Ac6UAY4wAUnNSnL'.
'0AlNYAWoQASmsAOVIAITGEtc4YWnsAUnsAMUqtvcaErcYAKUIA'.
'GCkAECHUyVh/AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAA'.
'AJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGxNUcXCT'.
'AAAAqUlEQVR4nI2Q1xKCMBREM2NHLCCogAGCjd6SqLT8/2cZKT'.
'6zb3tm987OBWCsXoejp8rC35fi4+l6gXFZlD0Rz6fZ1tdDmKR9'.
'RdOmkzmP7DDpilfX3SzvRgQ/Vr1uiZplfsCBiVf03RJd140wgj'.
'kmNqMtuYXcxyYmNWJdRoYwzpM9qRvGujuCmSR7q7ARY00/MiWk'.
'sCnjkobNEm1+HknDZgAqR0GKU43+wxdu2hYzbsHU6AAAAABJRU'.
'5ErkJggg==' ;
 
//==========================================================
// File: bs_lightred.png
//==========================================================
$this->imgdata_small[14][0]= 532 ;
$this->imgdata_small[14][1]=
'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAA3l'.
'BMVEX///////+/v7/Gvb0hGBj/5///3v//zu//1u//xucpGCG9'.
'nK21lKVSQkp7Wms5KTExISlaOUpjQlIhEBj/tdbOhKXnrcbGjK'.
'Wla4TetcbGnK2EWmv/rc73pcZ7UmOcY3vOpbW1jJzenLW9e5Rz'.
'Slq1c4xrQlJSOULGhJz/pcb3nL2chIzOnK33rcbelK3WjKWMWm'.
'vGe5SEUmM5ISnOtb3GrbXerb3vpb2ca3v/rcaUY3POhJxCKTF7'.
'SlrWnK21e4ytc4TvnLXnlK2la3taOUK1lJxrSlLGhJRjQkpSMT'.
'lw+q2nAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGjoP2Nm+AAAAr0'.
'lEQVR4nGNgIBaYiOk62imYwPnMkiIyso76yhJSzFARMxkRNk49'.
'a3t5OW6oFk1LVkYOfWUHKxUXiEYzLS12DnN3VXkjIRtFsIiSk5'.
'6evqGqhYGKugAfWMRa1FpD2UHeQEXQRlgALCJur+rgbCUNFOAS'.
'hqjRkZe3MpBTcwEKCEPMMTGSs3Xz8OQHCnBBHckt6OJpIyAMBD'.
'wwN/MYc4H4LK4wNzMwmGrzcvFqmxIdDgDiHRT6VVQkrAAAAABJ'.
'RU5ErkJggg==' ;
 
//==========================================================
// File: bxs_lightred.png
//==========================================================
$this->imgdata_xsmall[0][0]= 432 ;
$this->imgdata_xsmall[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAA3l'.
'BMVEX///////+/v7/Gvb0hGBj/5///3v//zu//1u//xucpGCG9'.
'nK21lKVSQkp7Wms5KTExISlaOUpjQlIhEBj/tdbOhKXnrcbGjK'.
'Wla4TetcbGnK2EWmv/rc73pcZ7UmOcY3vOpbW1jJzenLW9e5Rz'.
'Slq1c4xrQlJSOULGhJz/pcb3nL2chIzOnK33rcbelK3WjKWMWm'.
'vGe5SEUmM5ISnOtb3GrbXerb3vpb2ca3v/rcaUY3POhJxCKTF7'.
'SlrWnK21e4ytc4TvnLXnlK2la3taOUK1lJxrSlLGhJRjQkpSMT'.
'lw+q2nAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUKBOgGhWjAAAAS0'.
'lEQVR4nGNgQAEmunYmEJaMCKe1vBxYzJKVQ9lKBSSupKdnaKGi'.
'zgdkiqs6WKnYcIGYJnK2HvzCwmCNgi42wsLCECNMeXlNUY0HAL'.
'DaB7Du8MiEAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bxs_bluegreen.png
//==========================================================
$this->imgdata_xsmall[1][0]= 397 ;
$this->imgdata_xsmall[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAvV'.
'BMVEX///////+/v79j//855/8x3v851v9Spb1C1v8AOUqEtcZK'.
'lK1StdYxzv8hxv8AY4QASmNSlK1KpcZKtd4YQlIYnM4YrecIvf'.
'8AtfcAre8AjL0AhLUAc5wAa5QAWnsAQloAKTkAGCFKhJxKrdYY'.
'jL0Ypd4Atf8ArfcApecAnN4AlM4AjMYAe60Ac6UAY4wAUnNSnL'.
'0AlNYAWoQASmsAOVIAITGEtc4YWnsAUnsAMUqtvcaErcYAKUIA'.
'GCkAECHUyVh/AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAA'.
'AJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUKDVyF5Be'.
'AAAASUlEQVR4nGNgQAFmYqJcEJaEOJ+UrD5YTJKFTZrfGCQuaq'.
'glLWvMaQ5kqujo6hnbKIKYXPr68gp2dmCNJiZAlh3ECGsREWtU'.
'4wF1kwdpAHfnSwAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bxs_navy.png
//==========================================================
$this->imgdata_xsmall[2][0]= 353 ;
$this->imgdata_xsmall[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAk1'.
'BMVEX///////+/v7+trcbGxv+EhM6EhNaEhP97e/9zc/9ra/9S'.
'UsZKSrVSUs5jY/9SUtZKSsZSUudKSt5KSudKSv8YGIQpKf8YGK'.
'UYGN4YGO8YGPcQEP8ICP8AAP8AAPcAAO8AAOcAAN4AANYAAM4A'.
'AMYAAL0AALUAAK0AAKUAAJwAAJQAAIwAAIQAAHsAAHMAAGsAAG'.
'ONFkFbAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUJxXO4axZAAAAR0'.
'lEQVR4nGNgQAGskhKsEJaslIi8ijpYTJaDU1FVAyQuKSujoKKh'.
'LQ5kSigpqWro6oOYrOoaWroGBmCNWiCWAdQwUVFWVOMBOp4GCJ'.
's5S60AAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bxs_gray.png
//==========================================================
$this->imgdata_xsmall[3][0]= 492 ;
$this->imgdata_xsmall[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABI1'.
'BMVEX///8AAAD8EAD8IAD8NAD8RAD8VAAYGBi/v7+goKCCgoJk'.
'ZGRGRkb8yAD83AD87AD8/AD4+ADo+ADY+ADI+AC0+ACk+ACU+A'.
'CE+AB0/ABk/ABU/ABE/AAw/AAg/AAQ/AAA/AAA+AAA6BAA2CAA'.
'yDQAtEQApFQAlGQAhHQAdIgAZJgAVKgARLgAMMgAINwAEOwAAP'.
'wAAPgIAPAQAOgYAOAkANgsANA0AMg8AMBEALhMALBUAKhcAKBo'.
'AJhwAJB4AIiAAID////4+Pjy8vLs7Ozm5ubg4ODa2trT09PNzc'.
'3Hx8fBwcG7u7u1tbWurq6oqKiioqKcnJyWlpaQkJCJiYmDg4N9'.
'fX13d3dxcXFra2tkZGReXl5YWFhSUlJMTExGRkZAQEA1BLn4AA'.
'AAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEA'.
'AAsRAX9kX5EAAAAHdElNRQfTAwkUKC74clmyAAAAQklEQVR4nG'.
'NgQAVBYVCGt5dXYEQ0mOnp5h4QFgVmeri6+4dHxYMVeHoFRUTH'.
'gTUFBIZBWAwMkZEx8bFQM2Lj0UwHANc/DV6yq/BiAAAAAElFTk'.
'SuQmCC' ;
 
//==========================================================
// File: bxs_graypurple.png
//==========================================================
$this->imgdata_xsmall[4][0]= 542 ;
$this->imgdata_xsmall[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABSl'.
'BMVEX////////11P/MqdvKrNfAwMC+u7+9u7+4rr24lsi3rby3'.
'lMe1rLq1o720q7i0oL20ksSzoryyqbaykMGxlb2wkL+vnbiujb'.
'2sjLuri7qpl7GoirWoibenmK2mla6mjLKmhrSllauki7CjhrCj'.
'hLGihLChg6+ggq2fkqadkKOcfqqai6Gag6WYe6WXeqSWeaOTd6'.
'CTd5+Rdp6RdZ6RdZ2Qg5eOc5qMcpiLcZeJb5WIbpOHbZKGbJGE'.
'a4+CaY2AZ4t/Z4p/Zop/Zol+Zol7ZIZ6Y4V5YoR1ZH11X391Xn'.
'9zXX1yXXtxXHtvWnluWXhsV3VqVnNpVXJoVHFnU3BmUm9jUGth'.
'VGdgTmheTGZcS2RcSmRaSWJYR19XRl5SQllRQlhQQVdPQFZOP1'.
'VLPlFJO09IPE5IOk5FOEtEN0lDOEpDOElDNklCNkc/M0XhbrfD'.
'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
'EAAAsRAX9kX5EAAAAHdElNRQfTAwkUKCgREfyHAAAATUlEQVR4'.
'nGNgQAEcIko8EBY3M5Ougy+IxSXMwmTsFsAHZMqrSRvZB0W7A5'.
'k6FlYugXEZICaPr394Um4uSAFDRFRCbm4uxAihsDAhVOMBHT0L'.
'hkeRpo8AAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bxs_red.png
//==========================================================
$this->imgdata_xsmall[5][0]= 357 ;
$this->imgdata_xsmall[5][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAk1'.
'BMVEX////////GxsbGra3/xsbOhITWhIT/hIT/e3v/c3P/a2vG'.
'UlK1SkrOUlL/Y2PWUlLGSkrnUlLeSkrnSkr/SkqEGBj/KSmlGB'.
'jeGBjvGBj3GBj/EBD/CAj/AAD3AADvAADnAADeAADWAADOAADG'.
'AAC9AAC1AACtAAClAACcAACUAACMAACEAAB7AABzAABrAABjAA'.
'BuukXBAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUIyjy5SVMAAAAS0'.
'lEQVR4nGNgQAFsUpJsEJastIi8ijpYTJaDU0FVgxXIlJKVUVDR'.
'0BYHMiUUlVQ1dPVBTDZ1dS1dAwOQAgYtbSDLAGIEq6goK6rxAD'.
'yXBg73lwGUAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: bxs_yellow.png
//==========================================================
$this->imgdata_xsmall[6][0]= 414 ;
$this->imgdata_xsmall[6][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAzF'.
'BMVEX///////+/v79zYwCMewDOxoTWzoTezkr/5wj/5wDnzgDe'.
'xgC1pQCtnACllACcjACUhABjWgDGvVK1rUrOxlLGvUqEexilnB'.
'jv3hj35xj/7wj/7wD35wDv3gDn1gDezgDWxgDOvQDGtQC9rQCE'.
'ewB7cwBzawBrYwDWzlLn3lLe1krn3kre1hi9tQC1rQCtpQClnA'.
'CclACUjACMhAD/9wC/v7///8bOzoT//4T//3v//3P//2v//2Pn'.
'50r//0r//yn39xj//xD//wBjYwDO8noaAAAAAXRSTlMAQObYZg'.
'AAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAH'.
'dElNRQfTAwkUIzoBXFQEAAAAS0lEQVR4nGNgQAFsDhJsEJaTo5'.
'2skj5YzMnSSk7ZwBzIlOSUklPiMxYHMnW4FXT5VNVBTDZeXiNV'.
'QUGQAgYBYyBLEGIEq5gYK6rxAH4kBmHBaMQQAAAAAElFTkSuQm'.
'CC' ;
 
//==========================================================
// File: bxs_greenblue.png
//==========================================================
$this->imgdata_xsmall[7][0]= 410 ;
$this->imgdata_xsmall[7][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAxl'.
'BMVEX///////+/v79znJQhSkJ7raU5hHtjraVKnJRCjIRClIyU'.
'9++E595avbVaxr2/v7+ctbWcvb17nJxrjIx7paUxQkK9//+Mvb'.
'17ra2Evb17tbVCY2MQGBiU5+ec9/eM5+d71tZanJxjra1rvb1j'.
'tbVSnJxara1rzs5jxsZKlJRChIQpUlIhQkJatbVSpaU5c3MxY2'.
'MYMTEQISFavb1Sra1KnJxCjIw5e3sxa2spWlpClJQhSkoYOTkp'.
'Y2MhUlIQKSkIGBgQMTH+e30mAAAAAXRSTlMAQObYZgAAAAFiS0'.
'dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfT'.
'AwkUJy5/6kV9AAAATUlEQVR4nGNgQAGCyuyCEJaGugKHviVYzF'.
'hO3sxCWwDIVNLTM9PXtpEGMhW12Cy0DR1ATEFLSxZ7BweQAgYd'.
'HUMHBweIEQKiogKoxgMAo/4H5AfSehsAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bxs_purple.png
//==========================================================
$this->imgdata_xsmall[8][0]= 364 ;
$this->imgdata_xsmall[8][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAnF'.
'BMVEX///////+/v7/Gvca9rb3Grcb/xv+1hLWte629hL21e7XG'.
'hMbWhNbOe87We9b/hP//e/97OXv/c///a///Y/+cOZz/Sv/WOd'.
'bnOefvOe//Kf9jCGNrCGv/EP//CP/nCOf/AP/3APfvAO/nAOfe'.
'AN7WANbOAM7GAMa9AL21ALWtAK2lAKWcAJyUAJSMAIyEAIR7AH'.
'tzAHNrAGtjAGPP1sZnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
'HUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUIj'.
'mBTjT/AAAASUlEQVR4nGNgQAGskhKsEJaCrJiSuhZYTEFASFlD'.
'GyQuqSCnrK6tJwpkiquoamgbGIGYrFpaugbGxmCNunpAljHECB'.
'ZBQRZU4wFSMAZsXeM71AAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bxs_green.png
//==========================================================
$this->imgdata_xsmall[9][0]= 370 ;
$this->imgdata_xsmall[9][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAn1'.
'BMVEX///////+/v7+/v7/G/8aUxpSMvYyUzpSMzoyM1oxarVqE'.
'/4R7/3tavVpKnEpaxlpz/3Nr/2tKtUpj/2Na51pKzkpK1kpK50'.
'pK/0oYcxgp/ykYlBgY3hgY7xgY9xgQ/xAI/wgA/wAA9wAA7wAA'.
'5wAA3gAA1gAAzgAAxgAAvQAAtQAArQAApQAAnAAAlAAAjAAAhA'.
'AAewAAcwAAawAAYwA0tyxUAAAAAXRSTlMAQObYZgAAAAFiS0dE'.
'AIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAw'.
'kUKBrZxq0HAAAATElEQVR4nGNgQAGccrIcEJaivISyhjaIxa7I'.
'I6CiqcMKZMopKqho6OhLA5kyqmqaOobGICartraeoYkJSAGDnj'.
'6QZQIxgk1Skg3VeABlVgbItqEBUwAAAABJRU5ErkJggg==' ;
 
//==========================================================
// File: bxs_darkgreen.png
//==========================================================
$this->imgdata_xsmall[10][0]= 563 ;
$this->imgdata_xsmall[10][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABX1'.
'BMVEX////////l/+nAwMC86r+8wb28wby8wLy78sCzw7SywrSx'.
'wLKwvrGuvK+syK+ryq2rx62n36ym3aumxKmk2qij0Keh16ahva'.
'Og1aSguKKe06KeuaCetZ+d0KGdtZ+bz6Cay56ZyZ2Zwp2Zr5qZ'.
'rpqYwJuXyZuXrJmVw5mUxZiTxJeTw5eTq5WRwJWPtJKOvZKKuI'.
'6Kt42Kn4yJt42ItIuGsomFsYmEsIiEr4eDr4eBrIR/qoN+qIJ8'.
'poB7pH56o356on14nnt2nXl0mndzmnZzmXZymHVwlXNvlHJukn'.
'FtiHBqjm1qjW1oi2toiWpniWplh2hlhmdkhWdig2VggGNgf2Je'.
'fmFdfGBde19bbl1aeFxXdFpWclhVclhVcVdUcFZTb1VSbVRQal'.
'JPaVFKY0xKYkxJYUtIYEpHX0lEWkZCWERCV0NCVkM/U0A+U0A+'.
'UUA+UEA9Uj89UT48Tj45TDvewfrHAAAAAXRSTlMAQObYZgAAAA'.
'FiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElN'.
'RQfTAwkUKCFozUQjAAAATUlEQVR4nGNgQAGcoqrcEJYQB5OhSw'.
'CIxSXGwWThGcIDZCppK5o7hyV6AZl6NnbuoSmFICZ3YHB0RkkJ'.
'SAFDbEJaSUkJxAjeyEheVOMBQj4MOEkWew4AAAAASUVORK5CYI'.
'I=' ;
 
//==========================================================
// File: bxs_cyan.png
//==========================================================
$this->imgdata_xsmall[11][0]= 530 ;
$this->imgdata_xsmall[11][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABPl'.
'BMVEX////////F///AwMCvxsaC1NSC0dGCz8+CzMyA//94//91'.
'//9q//9j//9X4uJX09NXz89Xx8dXxMRL//9L5uZL3d1L2NhLxs'.
'ZLt7cv//8e9fUe8fEe7u4e398epqYehoYX//8L+PgK//8F9fUE'.
'/v4E5+cEb28EZ2cC//8C/v4C/f0CzMwCrq4Cjo4CdXUCaWkCZW'.
'UB/PwA//8A/f0A+/sA8/MA7e0A7OwA6+sA5eUA5OQA4uIA4eEA'.
'3NwA2toA2NgA1dUA09MA0tIA0NAAysoAxsYAxcUAxMQAv78Avr'.
'4AvLwAtrYAtbUAs7MAsLAAra0Aq6sAqKgApaUApKQAoqIAoKAA'.
'n58AmpoAlZUAk5MAkpIAkJAAj48AjIwAiYkAh4cAf38AfX0Ae3'.
'sAenoAcnIAcHAAa2sAaWkAaGgAYmIUPEuTAAAAAXRSTlMAQObY'.
'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAA'.
'AHdElNRQfTAwkUKQFKuFWqAAAATUlEQVR4nGNgQAGsUjJsEJaR'.
'grC5qz9YzIiL28YriB3IlDZRsnYNiZUDMmXtHT2CE9JBTDb/wI'.
'jkzEyQAoaomMTMzEyIERzy8hyoxgMAN2MLVPW0f4gAAAAASUVO'.
'RK5CYII=' ;
 
//==========================================================
// File: bxs_orange.png
//==========================================================
$this->imgdata_xsmall[12][0]= 572 ;
$this->imgdata_xsmall[12][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABaF'.
'BMVEX//////////8X/3oD/3nj/1HX/0Gr/xGP/rkv/gBf+iS/2'.
'bAL1agDxaQDuZwDrZwLpZQDmZQLlZADjcx7gZATeYQDdZgraXw'.
'DZXwHYXgDXiEvXZAvUjlfUXwXTjVfTbR7ShUvRbR7RWwDMWQDL'.
'WADKooLKWADJoYLJgkvHWATGoILFn4LFgEvFVgDEZx7EVQDDt6'.
'/DVQDCt6/CnoLChlfCVADAwMC+hFe+UgC8UgC6UQC4gVe4UAC3'.
'gVe3UAC1gFe1eUu1TwC1TgCzTgCwTQKuTACrSgCqSgCpSgCpSQ'.
'CodEulSACkRwCiRgCdRACcRACaQwCYQgCWQgKVQQCVQACUQACS'.
'UR6RPwCOPgCNPQCLPACKPACJOwCEOQCBOAB+NwB9NgB8NgB7NQ'.
'B6NwJ4NAB3RR52MwB0MgBuLwBtLwBsLwBqLgBpLQBkLQJiKgBh'.
'KgBgKwRcKABbKQJbJwBaKQRaJwBYKAJVJQDZvdIYAAAAAXRSTl'.
'MAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9k'.
'X5EAAAAHdElNRQfTAwkUJBSSy88MAAAATUlEQVR4nGNgQAGqwo'.
'paEBYPJ4eKezCIpc7HwmrqG6ENZMpLihm6RaWEAZl6Vo7ekRnF'.
'IKZWSHhcTnk5SAFDfFJWeXk5xAjj1FRjVOMBeFwNcWYSLjsAAA'.
'AASUVORK5CYII=' ;
 
//==========================================================
// File: bxs_lightblue.png
//==========================================================
$this->imgdata_xsmall[13][0]= 554 ;
$this->imgdata_xsmall[13][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABVl'.
'BMVEX////////d///AwMC7wcS08P+y+P+xxdCwxM+uws2twMur'.
'vsinzNynytylzuKhyN6e5v6d5P+d1fOcwNWcu8ub4f+at8iZ3v'.
'+ZvdGY2/yW2f+VscGU1vuT1fqTr72Sx+SSxeKR0fWRz/GPz/OP'.
'rr+OyeqMy+6Myu2LyeyKxueJudSGw+SGorGDvt+Cvd6CvN2Aud'.
'p+uNd+t9Z9tdV8tdR8tNN6sc94r813rct2q8h0qcZ0qMVzp8Rx'.
'o8Bwor5tn7ptnrptnrlsnbhqmbRpmbNpi51ol7Flkqtkkqtkka'.
'pjj6hijaRhjaZgi6NfiqJfiaFdh55bhJtag5pZgphYgJZYf5VX'.
'cn9Ve5FSeI1RdopRdYlQdYlPc4dPcoZPcoVNcINLboBLbH9GZn'.
'hGZXdFZHZEY3RDYnJCXW4/W2s/WWg+Wmo7VmU7VGM7U2E6VGM6'.
'VGI5UV82T1wGxheQAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
'gAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUJziL'.
'PvAsAAAATUlEQVR4nGNgQAHsQgqcEJYgG5Oegy+IxSHOxmTiFs'.
'gFZMprKBnbB8e7AplaFlbOQUl5ICanX0BEWmEhSAFDVGxKYWEh'.
'xAjusDBuVOMBJO8LrFHRAykAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: bxs_darkgray.png
//==========================================================
$this->imgdata_xsmall[14][0]= 574 ;
$this->imgdata_xsmall[14][1]=
'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABm'.
'JLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsRAAALEQF/ZF+RAAAB'.
'iElEQVR42k3QPU8TYRwA8P//ebkXrgdIColXRAOEkJqbaExMut'.
'DBhE1GNjYHPg+DG6ODiU6QOLjVxITBcFKBYCstlAC2Bz17fe76'.
'vLD6+wg/1FpTRFR5lpaub/u1eGBGaAT4HneD4OlXx7avtDYUjT'.
'HQabd2Ti8e3vVSKzxrtHS32wIpFVldno22Nqvvg2Bhl0gp/aNm'.
'vJ3qqXAtLIva+ks1H0wqlSXi4+d6+OFTfRsAfHJx2d1od24rZP'.
'xP2HzopINr1mkesX7ccojqif0v9crxWXODZTno3+dNGA7uWLsd'.
'mUYU4fHJCViMG9umLBmM4L6fagZGg9QKfjZ+Qfy3C3G/B3mugF'.
'IHHNcDf64E3KJALApk2p8CSolUUqLjFkyxOGMsTtFyJ+Wz57NQ'.
'8DghS4sLB0svioeZZo7nPhFoUKZDIVFbglkTTnl5/rC8snjAkJ'.
'Bk/XV5LxHC/v7tR8jzTFPbg8LENK9WX0Vv31T2AEmCSmlKCCoh'.
'ROnP1U1tPFYjJBRcbtzSf+GPsFTAQBq1n4AAAABKdEVYdHNpZ2'.
'5hdHVyZQBiYzYyMDIyNjgwYThjODMyMmUxNjk0NWUzZjljOGFh'.
'N2VmZWFhMjA4OTE2ZjkwOTdhZWE1MzYyMjk0MWRkM2I5EqaPDA'.
'AAAABJRU5ErkJggg==' ;
}
}
 
?>
/trunk/api/jpgraph_1.12.2/imgdata_diamonds.inc
New file
0,0 → 1,179
<?php
//=======================================================================
// File: IMGDATA_DIAMONDS.INC
// Description: Base64 encoded images for diamonds
// Created: 2003-03-20
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: imgdata_diamonds.inc,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2003 Johan Persson
//========================================================================
 
class ImgData_Diamonds extends ImgData {
var $name = 'Diamonds';
var $an = array(MARK_IMG_DIAMOND =>'imgdata');
var $colors = array('lightblue','darkblue','gray',
'blue','pink','purple','red','yellow');
var $index = array('lightblue' =>7,'darkblue'=>2,'gray'=>6,
'blue'=>4,'pink'=>1,'purple'=>5,'red'=>0,'yellow'=>3);
 
var $maxidx = 7 ;
var $imgdata ;
 
function ImgData_Diamonds() {
//==========================================================
// File: diam_red.png
//==========================================================
$this->imgdata[0][0]= 668 ;
$this->imgdata[0][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
'BMVEX///////+cAAD/AADOAABjAABrAADWGBjOCAj/CAj/GBj/'.
'EBCcCAiMOTl7KSl7ISFzGBilGBjOEBBrCAjv5+eMQkK1QkKtMT'.
'GtKSnWKSn/KSlzEBCcEBDexsb/tbXOe3ucWlqcUlKUSkr/e3vn'.
'a2u9UlL/a2uEMTHeUlLeSkqtOTn/UlL/SkrWOTn/QkL/OTmlIS'.
'H/MTH/ISH39/f/9/f35+fezs7/5+fvzs7WtbXOra3nvb3/zs7G'.
'nJzvtbXGlJTepaW9jIy1hITWlJS1e3uta2ulY2P/lJTnhITne3'.
'vGY2O9Wlr/c3PeY2O1Skr/Y2P/WlreQkLWISGlEBCglEUaAAAA'.
'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
'sSAdLdfvwAAAAHdElNRQfTAwsWEw5WI4qnAAABGUlEQVR4nHXQ'.
'1XLDMBAFUKUCM1NiO8zcpIxpp8z0//9SWY7b2LHv6EU6s1qtAN'.
'iMBAojLPkigpJvogKC4pxDuQipjanlICXof1RQDkYEF21mKIfg'.
'/GGKtjAmOKt9oSyuCU7OhyiDCQnjowGfRnooCJIkiWJvv8NxnG'.
'nyNAwFcekvZpPP3mu7Vrp8fOq8DYbTyjdnAvBj7Jbd7nP95urs'.
'+MC2D6unF+Cu0VJULQBAlsOQuueN3Hrp2nGUvqppemBZ0aU7Se'.
'SXvYZFMKaLJn7MH3btJmZEMEmGSOreqy0SI/4ffo3uiUOYEACy'.
'OFopmNWlP5uZd9uPWmUoxvK9ilO9NtBo6mS7KkZD0fOJYqgGBU'.
'S/T7OKCAA9tfsFOicXcbxt29cAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: diam_pink.png
//==========================================================
$this->imgdata[1][0]= 262 ;
$this->imgdata[1][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
'BMVEX///+AgID/M5n/Zpn/zMz/mZn1xELhAAAAAXRSTlMAQObY'.
'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
'AHdElNRQfTAwsWEi3tX8qUAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
'6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
'==' ;
 
//==========================================================
// File: diam_blue.png
//==========================================================
$this->imgdata[2][0]= 662 ;
$this->imgdata[2][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA+V'.
'BMVEX///+AgIAAAJwAAP8AAM4AAGMAAGsQEP8YGHMQEHMYGP8Q'.
'EKUICJwICM5KSpQxMYQpKXsYGNYQEM4ICGsICP97e85aWpw5OY'.
'xSUv85ObVCQt4xMa0pKa0hIaUpKf+9vd6EhLVra+dzc/9SUr1r'.
'a/9aWt5SUt5CQrVaWv9KSv8hIXs5Of8xMf8pKdYhIdYYGKUhIf'.
'/Ozs739//v7/fn5+/v7//n5/fW1ufOzufOzu/W1v+trc69veel'.
'pc6trd6UlMa9vf+MjL21tfe1tf+UlNZzc61ra6Wlpf+EhOeMjP'.
'9ra8ZSUpyEhP9CQoxKSrVCQv85Od4xMdYQENZnJhlWAAAAAXRS'.
'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
'LdfvwAAAAHdElNRQfTAwsWEx3Snct5AAABFklEQVR4nHXR5XbD'.
'IBgGYM6AuHsaqbvOfeuknev9X8xISbplSd5/8JyXwwcA/I0AKm'.
'PFchVBdvKNKggKQx2VIoRwMZihMiQE49YUlWBCcPL0hYq4ITh+'.
'qKECUoLDZWqoQNA766F/mJHlHXblPJJNiyURhM5eU9cNw5BlmS'.
'IrLOLxhzfotF7vwO2j3ez2ap/TmW4AIM7DoN9+tu+vLk6Pdg9O'.
'6ufXjfXLm6pxPACSJIpRFAa+/26DhuK6qjbiON40k0N3skjOvm'.
'NijBmchF5mi+1jhQqDmWyIzPp1hUlrv8On5l+6mMm1tigFNyrt'.
'5R97g+FKKyGKkTNKesXPJTZXOFIrUoKiypcTQVHjK4g8H2dWEQ'.
'B8bvUDLSQXSr41rmEAAAAASUVORK5CYII=' ;
 
//==========================================================
// File: diam_yellow.png
//==========================================================
$this->imgdata[3][0]= 262 ;
$this->imgdata[3][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
'BMVEX///+AgIBmMwCZZgD/zADMmQD/QLMZAAAAAXRSTlMAQObY'.
'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
'AHdElNRQfTAwsWEwcv/zIDAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
'6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
'==' ;
 
//==========================================================
// File: diam_lightblue.png
//==========================================================
$this->imgdata[4][0]= 671 ;
$this->imgdata[4][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/1'.
'BMVEX///+AgIAAnP8A//8Azv8AY/8Aa/8I//8Y1v8Izv8Y//8Q'.
'//8InP8Qzv8Ypf85jP8he/8Yc/8Ia/8pe/8p//8p1v9Ctf8xrf'.
'8prf8QnP8Qc/9CjP+1//97//9r//9S//9K//9C//85//8x//8h'.
'//9r5/9K3v9S3v851v97zv9Svf85rf8hpf/G3v9SnP9anP9KlP'.
'8xhP/n7//v7+f3///n///O//+U//9z//9j//9a//975/9C3v8h'.
'1v+E5/+17/9j3v/O7//n9/+95/+l3v9jxv+U1v8Qpf9avf9Ktf'.
'+Uxv+11v97tf9rrf+cxv+Mvf9jpf+tzv+Etf/O3v/39/8Akkxr'.
'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
'IAAAsSAdLdfvwAAAAHdElNRQfTAwsWEiHk6Ya/AAABGUlEQVR4'.
'nHXQ13KDMBAF0J2o0E01GHDvJa7p3em95/+/JQJMYjDc0Yt0Zr'.
'VaAaxHgtxwbSGPkGQpOIeQ2ORxJiJmNWYZyAhZR0WcgQGhViU0'.
'nEGoedDHGxgRapRPcRpXhOr7XZzCmLjaXk9IIjvkOEmSRLG62+'.
'F5XlEElhA5sW21GvXj6mGlDBfnJ51lr9svnvEKwH1hu2QPbwd3'.
'N9eXVzuL7/Hn29frdKaamgcgy67L3HFG9gDefV+dm5qme4YRXL'.
'oVR374mRqUELZYosf84XAxISFRQuMh4rrH8YxGSP6HX6H97NNQ'.
'KEAaR08qCeuSnx2a8zIPWqUowtKHSRK91rAw0elmVYQFVc8mhq'.
'7p5RD7Ps3IIwA9sfsFxFUX6eZ4Zh4AAAAASUVORK5CYII=' ;
 
//==========================================================
// File: diam_purple.png
//==========================================================
$this->imgdata[5][0]= 657 ;
$this->imgdata[5][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
'BMVEX///////8xAP/OAP+cAP9jAP9rAP+cCP85CP/OEP9SKf/O'.
'CP9CEP9zGP9rCP+lGP/WOf/WIf9KIf9jOf+MQv+EMf97If9zEP'.
'+1Sv+lIf/ne//eUv/na//n5//Oxv/Wzv+chP9zUv97Wv9rQv9a'.
'Mf9KGP/v5/+te/97Kf+9Y/+tOf+tKf+lEP/vtf/WMf/WKf/v7+'.
'f39/+tnP+9rf9rSv9jQv9CGP+ljP+EY//Gtf+tlP+Ma/9zSv/e'.
'zv+UUv+9lP+cWv+lY/+cUv+MOf+EKf+UQv/Opf/OhP/Ga/+1Qv'.
'/Oe/+9Uv/ntf/eWv/eSv/WGP/3zv/vlP/WEP//9/+pL4oHAAAA'.
'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
'sSAdLdfvwAAAAHdElNRQfTAwsWEjX+M1LCAAABDklEQVR4nHXQ'.
'1bLDIBAGYFqIEW+ksbr7cXd3ff93OUCamdOE/Mxw882yywLwPz'.
'+gNKotlRFUVnNUQlCxTMRFCKEdE+MgpJaEiIOU4DKaoSIygtb3'.
'FBUQrm3xjPK4JvXjK0A5hFniYSBtIilQVYUm+X0KTVNiYah+2q'.
'ulFb8nUbSovD2+TCavwXQWmnMA6ro+di+uR5cPzfPhVqPV3N1p'.
'n3b3+rimAWAYhP3xnXd7P6oc9vadPsa1wYEs00dFQRAFehlX21'.
'25Sg9NOgwF5jeNTjVL9om0TjDc1lmeCKZ17nFPzhPtSRt6J06R'.
'WKUoeG3MoXRa/wjLHGLodwZcotPqjsYngnWslRBZH91hWTbpD2'.
'EdF1ECWW1SAAAAAElFTkSuQmCC' ;
 
//==========================================================
// File: diam_gray.png
//==========================================================
$this->imgdata[6][0]= 262 ;
$this->imgdata[6][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
'BMVEX//////wAzMzNmZmbMzMyZmZlq4Qo5AAAAAXRSTlMAQObY'.
'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
'AHdElNRQfTAwsWExZFTxLxAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
'6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
'==' ;
 
//==========================================================
// File: diam_blgr.png
//==========================================================
$this->imgdata[7][0]= 262 ;
$this->imgdata[7][1]=
'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
'BMVEX///+AgIBmzP9m///M//+Z//8hMmBVAAAAAXRSTlMAQObY'.
'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
'AHdElNRQfTAwsWEwCxm6egAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
'6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
'==' ;
}
}
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_gradient.php
New file
0,0 → 1,246
<?php
/*=======================================================================
// File: JPGRAPH_GRADIENT.PHP
// Description: Create a color gradient
// Created: 2003-02-01
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_gradient.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL
// Copyright (C) 2003 Johan Persson
//========================================================================
*/
 
//===================================================
// CLASS Gradient
// Description: Handles gradient fills. This is to be
// considered a "friend" class of Class Image.
//===================================================
class Gradient {
var $img=null;
//---------------
// CONSTRUCTOR
function Gradient(&$img) {
$this->img = $img;
}
 
//---------------
// PUBLIC METHODS
// Produce a gradient filled rectangle with a smooth transition between
// two colors.
// ($xl,$yt) Top left corner
// ($xr,$yb) Bottom right
// $from_color Starting color in gradient
// $to_color End color in the gradient
// $style Which way is the gradient oriented?
function FilledRectangle($xl,$yt,$xr,$yb,$from_color,$to_color,$style=1) {
switch( $style ) {
case 1: // HORIZONTAL
$steps = abs($xr-$xl);
$delta = $xr>=$xl ? 1 : -1;
$this->GetColArray($from_color,$to_color,$steps,$colors);
for( $i=0, $x=$xl; $i<$steps; ++$i ) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yt,$x,$yb);
$x += $delta;
}
break;
 
case 2: // VERTICAL
$steps = abs($yb-$yt);
$delta = $yb>=$yt ? 1 : -1;
$this->GetColArray($from_color,$to_color,$steps,$colors);
for($i=0,$y=$yt; $i<$steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($xl,$y,$xr,$y);
$y += $delta;
}
break;
 
case 3: // VERTICAL FROM MIDDLE
$steps = abs($yb-$yt)/2;
$delta = $yb>=$yt ? 1 : -1;
$this->GetColArray($from_color,$to_color,$steps,$colors);
for($y=$yt, $i=0; $i < $steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($xl,$y,$xr,$y);
$y += $delta;
}
--$i;
for($j=0; $j < $steps; ++$j, --$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($xl,$y,$xr,$y);
$y += $delta;
}
$this->img->Line($xl,$y,$xr,$y);
break;
 
case 4: // HORIZONTAL FROM MIDDLE
$steps = abs($xr-$xl)/2;
$delta = $xr>=$xl ? 1 : -1;
$this->GetColArray($from_color,$to_color,$steps,$colors);
for($x=$xl, $i=0; $i<$steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
--$i;
for($j=0; $j<$steps; ++$j, --$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
$this->img->Line($x,$yb,$x,$yt);
break;
 
case 6: // HORIZONTAL WIDER MIDDLE
$steps = abs($xr-$xl)/3;
$delta = $xr>=$xl ? 1 : -1;
$this->GetColArray($from_color,$to_color,$steps,$colors);
for($x=$xl, $i=0; $i < $steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
--$i;
$this->img->current_color = $colors[$i];
for($j=0; $j< $steps; ++$j) {
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
for($j=0; $j<$steps; ++$j, --$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
break;
 
case 8: // LEFT REFLECTION
$steps1 = round(0.3*abs($xr-$xl));
$delta = $xr>=$xl ? 1 : -1;
 
$this->GetColArray($from_color.':1.3',$to_color,$steps1,$colors);
for($x=$xl, $i=0; $i < $steps1; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
$steps2 = max(1,round(0.08*abs($xr-$xl)));
$this->img->SetColor($to_color);
for($j=0; $j< $steps2; ++$j) {
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
$steps = abs($xr-$xl)-$steps1-$steps2;
$this->GetColArray($to_color,$from_color,$steps,$colors);
for($i=0; $i < $steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
break;
 
case 9: // RIGHT REFLECTION
$steps1 = round(0.7*abs($xr-$xl));
$delta = $xr>=$xl ? 1 : -1;
 
$this->GetColArray($from_color,$to_color,$steps1,$colors);
for($x=$xl, $i=0; $i < $steps1; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
$steps2 = max(1,round(0.08*abs($xr-$xl)));
$this->img->SetColor($to_color);
for($j=0; $j< $steps2; ++$j) {
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
$steps = abs($xr-$xl)-$steps1-$steps2;
$this->GetColArray($to_color,$from_color.':1.3',$steps,$colors);
for($i=0; $i < $steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($x,$yb,$x,$yt);
$x += $delta;
}
break;
 
 
 
case 7: // VERTICAL WIDER MIDDLE
$steps = abs($yb-$yt)/3;
$delta = $yb>=$yt? 1 : -1;
$this->GetColArray($from_color,$to_color,$steps,$colors);
for($y=$yt, $i=0; $i<$steps; ++$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($xl,$y,$xr,$y);
$y += $delta;
}
--$i;
$this->img->current_color = $colors[$i];
for($j=0; $j< $steps; ++$j) {
$this->img->Line($xl,$y,$xr,$y);
$y += $delta;
}
for($j=0; $j<$steps; ++$j, --$i) {
$this->img->current_color = $colors[$i];
$this->img->Line($xl,$y,$xr,$y);
$y += $delta;
}
break;
 
case 5: // Rectangle
$steps = floor(min(($yb-$yt)+1,($xr-$xl)+1)/2);
$this->GetColArray($from_color,$to_color,$steps,$colors);
$dx = ($xr-$xl)/2;
$dy = ($yb-$yt)/2;
$x=$xl;$y=$yt;$x2=$xr;$y2=$yb;
for($x=$xl, $i=0; $x<$xl+$dx && $y<$yt+$dy ; ++$x, ++$y, --$x2, --$y2, ++$i) {
assert($i<count($colors));
$this->img->current_color = $colors[$i];
$this->img->Rectangle($x,$y,$x2,$y2);
}
$this->img->Line($x,$y,$x2,$y2);
break;
 
default:
die("JpGraph Error: Unknown gradient style (=$style).");
break;
}
}
 
//---------------
// PRIVATE METHODS
// Add to the image color map the necessary colors to do the transition
// between the two colors using $numcolors intermediate colors
function GetColArray($from_color,$to_color,$arr_size,&$colors,$numcols=100) {
if( $arr_size==0 ) return;
// If color is given as text get it's corresponding r,g,b values
$from_color = $this->img->rgb->Color($from_color);
$to_color = $this->img->rgb->Color($to_color);
$rdelta=($to_color[0]-$from_color[0])/$numcols;
$gdelta=($to_color[1]-$from_color[1])/$numcols;
$bdelta=($to_color[2]-$from_color[2])/$numcols;
$colorsperstep = $numcols/$arr_size;
$prevcolnum = -1;
for ($i=0; $i<$arr_size; ++$i) {
$colnum = floor($colorsperstep*$i);
if ( $colnum == $prevcolnum )
$colors[$i] = $colidx;
else {
$r = floor($from_color[0] + $colnum*$rdelta);
$g = floor($from_color[1] + $colnum*$gdelta);
$b = floor($from_color[2] + $colnum*$bdelta);
$colidx = $this->img->rgb->Allocate(sprintf("#%02x%02x%02x",$r,$g,$b));
$colors[$i] = $colidx;
}
$prevcolnum = $colnum;
}
}
} // Class
 
?>
/trunk/api/jpgraph_1.12.2/jpgraph_gb2312.php
New file
0,0 → 1,1554
<?php
//=======================================================================
// File: JPGRAPH_GB2312.PHP
// Description: PHP4 Graph Plotting library. Chinese font conversions
// Created: 2003-05-30
// Author: Johan Persson (johanp@aditus.nu)
// Ver: $Id: jpgraph_gb2312.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License: This code is released under QPL 1.0
// Copyright (C) 2001,2002,2003 Johan Persson
//========================================================================
 
 
class GB2312toUTF8 {
// --------------------------------------------------------------------
// This code table is used to translate GB2312 code (key) to
// it's corresponding Unicode value (data)
// --------------------------------------------------------------------
var $codetable = array(
8481 => 12288, 8482 => 12289, 8483 => 12290, 8484 => 12539, 8485 => 713,
8486 => 711, 8487 => 168, 8488 => 12291, 8489 => 12293, 8490 => 8213,
8491 => 65374, 8492 => 8214, 8493 => 8230, 8494 => 8216, 8495 => 8217,
8496 => 8220, 8497 => 8221, 8498 => 12308, 8499 => 12309, 8500 => 12296,
8501 => 12297, 8502 => 12298, 8503 => 12299, 8504 => 12300, 8505 => 12301,
8506 => 12302, 8507 => 12303, 8508 => 12310, 8509 => 12311, 8510 => 12304,
8511 => 12305, 8512 => 177, 8513 => 215, 8514 => 247, 8515 => 8758,
8516 => 8743, 8517 => 8744, 8518 => 8721, 8519 => 8719, 8520 => 8746,
8521 => 8745, 8522 => 8712, 8523 => 8759, 8524 => 8730, 8525 => 8869,
8526 => 8741, 8527 => 8736, 8528 => 8978, 8529 => 8857, 8530 => 8747,
8531 => 8750, 8532 => 8801, 8533 => 8780, 8534 => 8776, 8535 => 8765,
8536 => 8733, 8537 => 8800, 8538 => 8814, 8539 => 8815, 8540 => 8804,
8541 => 8805, 8542 => 8734, 8543 => 8757, 8544 => 8756, 8545 => 9794,
8546 => 9792, 8547 => 176, 8548 => 8242, 8549 => 8243, 8550 => 8451,
8551 => 65284, 8552 => 164, 8553 => 65504, 8554 => 65505, 8555 => 8240,
8556 => 167, 8557 => 8470, 8558 => 9734, 8559 => 9733, 8560 => 9675,
8561 => 9679, 8562 => 9678, 8563 => 9671, 8564 => 9670, 8565 => 9633,
8566 => 9632, 8567 => 9651, 8568 => 9650, 8569 => 8251, 8570 => 8594,
8571 => 8592, 8572 => 8593, 8573 => 8595, 8574 => 12307, 8753 => 9352,
8754 => 9353, 8755 => 9354, 8756 => 9355, 8757 => 9356, 8758 => 9357,
8759 => 9358, 8760 => 9359, 8761 => 9360, 8762 => 9361, 8763 => 9362,
8764 => 9363, 8765 => 9364, 8766 => 9365, 8767 => 9366, 8768 => 9367,
8769 => 9368, 8770 => 9369, 8771 => 9370, 8772 => 9371, 8773 => 9332,
8774 => 9333, 8775 => 9334, 8776 => 9335, 8777 => 9336, 8778 => 9337,
8779 => 9338, 8780 => 9339, 8781 => 9340, 8782 => 9341, 8783 => 9342,
8784 => 9343, 8785 => 9344, 8786 => 9345, 8787 => 9346, 8788 => 9347,
8789 => 9348, 8790 => 9349, 8791 => 9350, 8792 => 9351, 8793 => 9312,
8794 => 9313, 8795 => 9314, 8796 => 9315, 8797 => 9316, 8798 => 9317,
8799 => 9318, 8800 => 9319, 8801 => 9320, 8802 => 9321, 8805 => 12832,
8806 => 12833, 8807 => 12834, 8808 => 12835, 8809 => 12836, 8810 => 12837,
8811 => 12838, 8812 => 12839, 8813 => 12840, 8814 => 12841, 8817 => 8544,
8818 => 8545, 8819 => 8546, 8820 => 8547, 8821 => 8548, 8822 => 8549,
8823 => 8550, 8824 => 8551, 8825 => 8552, 8826 => 8553, 8827 => 8554,
8828 => 8555, 8993 => 65281, 8994 => 65282, 8995 => 65283, 8996 => 65509,
8997 => 65285, 8998 => 65286, 8999 => 65287, 9000 => 65288, 9001 => 65289,
9002 => 65290, 9003 => 65291, 9004 => 65292, 9005 => 65293, 9006 => 65294,
9007 => 65295, 9008 => 65296, 9009 => 65297, 9010 => 65298, 9011 => 65299,
9012 => 65300, 9013 => 65301, 9014 => 65302, 9015 => 65303, 9016 => 65304,
9017 => 65305, 9018 => 65306, 9019 => 65307, 9020 => 65308, 9021 => 65309,
9022 => 65310, 9023 => 65311, 9024 => 65312, 9025 => 65313, 9026 => 65314,
9027 => 65315, 9028 => 65316, 9029 => 65317, 9030 => 65318, 9031 => 65319,
9032 => 65320, 9033 => 65321, 9034 => 65322, 9035 => 65323, 9036 => 65324,
9037 => 65325, 9038 => 65326, 9039 => 65327, 9040 => 65328, 9041 => 65329,
9042 => 65330, 9043 => 65331, 9044 => 65332, 9045 => 65333, 9046 => 65334,
9047 => 65335, 9048 => 65336, 9049 => 65337, 9050 => 65338, 9051 => 65339,
9052 => 65340, 9053 => 65341, 9054 => 65342, 9055 => 65343, 9056 => 65344,
9057 => 65345, 9058 => 65346, 9059 => 65347, 9060 => 65348, 9061 => 65349,
9062 => 65350, 9063 => 65351, 9064 => 65352, 9065 => 65353, 9066 => 65354,
9067 => 65355, 9068 => 65356, 9069 => 65357, 9070 => 65358, 9071 => 65359,
9072 => 65360, 9073 => 65361, 9074 => 65362, 9075 => 65363, 9076 => 65364,
9077 => 65365, 9078 => 65366, 9079 => 65367, 9080 => 65368, 9081 => 65369,
9082 => 65370, 9083 => 65371, 9084 => 65372, 9085 => 65373, 9086 => 65507,
9249 => 12353, 9250 => 12354, 9251 => 12355, 9252 => 12356, 9253 => 12357,
9254 => 12358, 9255 => 12359, 9256 => 12360, 9257 => 12361, 9258 => 12362,
9259 => 12363, 9260 => 12364, 9261 => 12365, 9262 => 12366, 9263 => 12367,
9264 => 12368, 9265 => 12369, 9266 => 12370, 9267 => 12371, 9268 => 12372,
9269 => 12373, 9270 => 12374, 9271 => 12375, 9272 => 12376, 9273 => 12377,
9274 => 12378, 9275 => 12379, 9276 => 12380, 9277 => 12381, 9278 => 12382,
9279 => 12383, 9280 => 12384, 9281 => 12385, 9282 => 12386, 9283 => 12387,
9284 => 12388, 9285 => 12389, 9286 => 12390, 9287 => 12391, 9288 => 12392,
9289 => 12393, 9290 => 12394, 9291 => 12395, 9292 => 12396, 9293 => 12397,
9294 => 12398, 9295 => 12399, 9296 => 12400, 9297 => 12401, 9298 => 12402,
9299 => 12403, 9300 => 12404, 9301 => 12405, 9302 => 12406, 9303 => 12407,
9304 => 12408, 9305 => 12409, 9306 => 12410, 9307 => 12411, 9308 => 12412,
9309 => 12413, 9310 => 12414, 9311 => 12415, 9312 => 12416, 9313 => 12417,
9314 => 12418, 9315 => 12419, 9316 => 12420, 9317 => 12421, 9318 => 12422,
9319 => 12423, 9320 => 12424, 9321 => 12425, 9322 => 12426, 9323 => 12427,
9324 => 12428, 9325 => 12429, 9326 => 12430, 9327 => 12431, 9328 => 12432,
9329 => 12433, 9330 => 12434, 9331 => 12435, 9505 => 12449, 9506 => 12450,
9507 => 12451, 9508 => 12452, 9509 => 12453, 9510 => 12454, 9511 => 12455,
9512 => 12456, 9513 => 12457, 9514 => 12458, 9515 => 12459, 9516 => 12460,
9517 => 12461, 9518 => 12462, 9519 => 12463, 9520 => 12464, 9521 => 12465,
9522 => 12466, 9523 => 12467, 9524 => 12468, 9525 => 12469, 9526 => 12470,
9527 => 12471, 9528 => 12472, 9529 => 12473, 9530 => 12474, 9531 => 12475,
9532 => 12476, 9533 => 12477, 9534 => 12478, 9535 => 12479, 9536 => 12480,
9537 => 12481, 9538 => 12482, 9539 => 12483, 9540 => 12484, 9541 => 12485,
9542 => 12486, 9543 => 12487, 9544 => 12488, 9545 => 12489, 9546 => 12490,
9547 => 12491, 9548 => 12492, 9549 => 12493, 9550 => 12494, 9551 => 12495,
9552 => 12496, 9553 => 12497, 9554 => 12498, 9555 => 12499, 9556 => 12500,
9557 => 12501, 9558 => 12502, 9559 => 12503, 9560 => 12504, 9561 => 12505,
9562 => 12506, 9563 => 12507, 9564 => 12508, 9565 => 12509, 9566 => 12510,
9567 => 12511, 9568 => 12512, 9569 => 12513, 9570 => 12514, 9571 => 12515,
9572 => 12516, 9573 => 12517, 9574 => 12518, 9575 => 12519, 9576 => 12520,
9577 => 12521, 9578 => 12522, 9579 => 12523, 9580 => 12524, 9581 => 12525,
9582 => 12526, 9583 => 12527, 9584 => 12528, 9585 => 12529, 9586 => 12530,
9587 => 12531, 9588 => 12532, 9589 => 12533, 9590 => 12534, 9761 => 913,
9762 => 914, 9763 => 915, 9764 => 916, 9765 => 917, 9766 => 918,
9767 => 919, 9768 => 920, 9769 => 921, 9770 => 922, 9771 => 923,
9772 => 924, 9773 => 925, 9774 => 926, 9775 => 927, 9776 => 928,
9777 => 929, 9778 => 931, 9779 => 932, 9780 => 933, 9781 => 934,
9782 => 935, 9783 => 936, 9784 => 937, 9793 => 945, 9794 => 946,
9795 => 947, 9796 => 948, 9797 => 949, 9798 => 950, 9799 => 951,
9800 => 952, 9801 => 953, 9802 => 954, 9803 => 955, 9804 => 956,
9805 => 957, 9806 => 958, 9807 => 959, 9808 => 960, 9809 => 961,
9810 => 963, 9811 => 964, 9812 => 965, 9813 => 966, 9814 => 967,
9815 => 968, 9816 => 969, 10017 => 1040, 10018 => 1041, 10019 => 1042,
10020 => 1043, 10021 => 1044, 10022 => 1045, 10023 => 1025, 10024 => 1046,
10025 => 1047, 10026 => 1048, 10027 => 1049, 10028 => 1050, 10029 => 1051,
10030 => 1052, 10031 => 1053, 10032 => 1054, 10033 => 1055, 10034 => 1056,
10035 => 1057, 10036 => 1058, 10037 => 1059, 10038 => 1060, 10039 => 1061,
10040 => 1062, 10041 => 1063, 10042 => 1064, 10043 => 1065, 10044 => 1066,
10045 => 1067, 10046 => 1068, 10047 => 1069, 10048 => 1070, 10049 => 1071,
10065 => 1072, 10066 => 1073, 10067 => 1074, 10068 => 1075, 10069 => 1076,
10070 => 1077, 10071 => 1105, 10072 => 1078, 10073 => 1079, 10074 => 1080,
10075 => 1081, 10076 => 1082, 10077 => 1083, 10078 => 1084, 10079 => 1085,
10080 => 1086, 10081 => 1087, 10082 => 1088, 10083 => 1089, 10084 => 1090,
10085 => 1091, 10086 => 1092, 10087 => 1093, 10088 => 1094, 10089 => 1095,
10090 => 1096, 10091 => 1097, 10092 => 1098, 10093 => 1099, 10094 => 1100,
10095 => 1101, 10096 => 1102, 10097 => 1103, 10273 => 257, 10274 => 225,
10275 => 462, 10276 => 224, 10277 => 275, 10278 => 233, 10279 => 283,
10280 => 232, 10281 => 299, 10282 => 237, 10283 => 464, 10284 => 236,
10285 => 333, 10286 => 243, 10287 => 466, 10288 => 242, 10289 => 363,
10290 => 250, 10291 => 468, 10292 => 249, 10293 => 470, 10294 => 472,
10295 => 474, 10296 => 476, 10297 => 252, 10298 => 234, 10309 => 12549,
10310 => 12550, 10311 => 12551, 10312 => 12552, 10313 => 12553, 10314 => 12554,
10315 => 12555, 10316 => 12556, 10317 => 12557, 10318 => 12558, 10319 => 12559,
10320 => 12560, 10321 => 12561, 10322 => 12562, 10323 => 12563, 10324 => 12564,
10325 => 12565, 10326 => 12566, 10327 => 12567, 10328 => 12568, 10329 => 12569,
10330 => 12570, 10331 => 12571, 10332 => 12572, 10333 => 12573, 10334 => 12574,
10335 => 12575, 10336 => 12576, 10337 => 12577, 10338 => 12578, 10339 => 12579,
10340 => 12580, 10341 => 12581, 10342 => 12582, 10343 => 12583, 10344 => 12584,
10345 => 12585, 10532 => 9472, 10533 => 9473, 10534 => 9474, 10535 => 9475,
10536 => 9476, 10537 => 9477, 10538 => 9478, 10539 => 9479, 10540 => 9480,
10541 => 9481, 10542 => 9482, 10543 => 9483, 10544 => 9484, 10545 => 9485,
10546 => 9486, 10547 => 9487, 10548 => 9488, 10549 => 9489, 10550 => 9490,
10551 => 9491, 10552 => 9492, 10553 => 9493, 10554 => 9494, 10555 => 9495,
10556 => 9496, 10557 => 9497, 10558 => 9498, 10559 => 9499, 10560 => 9500,
10561 => 9501, 10562 => 9502, 10563 => 9503, 10564 => 9504, 10565 => 9505,
10566 => 9506, 10567 => 9507, 10568 => 9508, 10569 => 9509, 10570 => 9510,
10571 => 9511, 10572 => 9512, 10573 => 9513, 10574 => 9514, 10575 => 9515,
10576 => 9516, 10577 => 9517, 10578 => 9518, 10579 => 9519, 10580 => 9520,
10581 => 9521, 10582 => 9522, 10583 => 9523, 10584 => 9524, 10585 => 9525,
10586 => 9526, 10587 => 9527, 10588 => 9528, 10589 => 9529, 10590 => 9530,
10591 => 9531, 10592 => 9532, 10593 => 9533, 10594 => 9534, 10595 => 9535,
10596 => 9536, 10597 => 9537, 10598 => 9538, 10599 => 9539, 10600 => 9540,
10601 => 9541, 10602 => 9542, 10603 => 9543, 10604 => 9544, 10605 => 9545,
10606 => 9546, 10607 => 9547, 12321 => 21834, 12322 => 38463, 12323 => 22467,
12324 => 25384, 12325 => 21710, 12326 => 21769, 12327 => 21696, 12328 => 30353,
12329 => 30284, 12330 => 34108, 12331 => 30702, 12332 => 33406, 12333 => 30861,
12334 => 29233, 12335 => 38552, 12336 => 38797, 12337 => 27688, 12338 => 23433,
12339 => 20474, 12340 => 25353, 12341 => 26263, 12342 => 23736, 12343 => 33018,
12344 => 26696, 12345 => 32942, 12346 => 26114, 12347 => 30414, 12348 => 20985,
12349 => 25942, 12350 => 29100, 12351 => 32753, 12352 => 34948, 12353 => 20658,
12354 => 22885, 12355 => 25034, 12356 => 28595, 12357 => 33453, 12358 => 25420,
12359 => 25170, 12360 => 21485, 12361 => 21543, 12362 => 31494, 12363 => 20843,
12364 => 30116, 12365 => 24052, 12366 => 25300, 12367 => 36299, 12368 => 38774,
12369 => 25226, 12370 => 32793, 12371 => 22365, 12372 => 38712, 12373 => 32610,
12374 => 29240, 12375 => 30333, 12376 => 26575, 12377 => 30334, 12378 => 25670,
12379 => 20336, 12380 => 36133, 12381 => 25308, 12382 => 31255, 12383 => 26001,
12384 => 29677, 12385 => 25644, 12386 => 25203, 12387 => 33324, 12388 => 39041,
12389 => 26495, 12390 => 29256, 12391 => 25198, 12392 => 25292, 12393 => 20276,
12394 => 29923, 12395 => 21322, 12396 => 21150, 12397 => 32458, 12398 => 37030,
12399 => 24110, 12400 => 26758, 12401 => 27036, 12402 => 33152, 12403 => 32465,
12404 => 26834, 12405 => 30917, 12406 => 34444, 12407 => 38225, 12408 => 20621,
12409 => 35876, 12410 => 33502, 12411 => 32990, 12412 => 21253, 12413 => 35090,
12414 => 21093, 12577 => 34180, 12578 => 38649, 12579 => 20445, 12580 => 22561,
12581 => 39281, 12582 => 23453, 12583 => 25265, 12584 => 25253, 12585 => 26292,
12586 => 35961, 12587 => 40077, 12588 => 29190, 12589 => 26479, 12590 => 30865,
12591 => 24754, 12592 => 21329, 12593 => 21271, 12594 => 36744, 12595 => 32972,
12596 => 36125, 12597 => 38049, 12598 => 20493, 12599 => 29384, 12600 => 22791,
12601 => 24811, 12602 => 28953, 12603 => 34987, 12604 => 22868, 12605 => 33519,
12606 => 26412, 12607 => 31528, 12608 => 23849, 12609 => 32503, 12610 => 29997,
12611 => 27893, 12612 => 36454, 12613 => 36856, 12614 => 36924, 12615 => 40763,
12616 => 27604, 12617 => 37145, 12618 => 31508, 12619 => 24444, 12620 => 30887,
12621 => 34006, 12622 => 34109, 12623 => 27605, 12624 => 27609, 12625 => 27606,
12626 => 24065, 12627 => 24199, 12628 => 30201, 12629 => 38381, 12630 => 25949,
12631 => 24330, 12632 => 24517, 12633 => 36767, 12634 => 22721, 12635 => 33218,
12636 => 36991, 12637 => 38491, 12638 => 38829, 12639 => 36793, 12640 => 32534,
12641 => 36140, 12642 => 25153, 12643 => 20415, 12644 => 21464, 12645 => 21342,
12646 => 36776, 12647 => 36777, 12648 => 36779, 12649 => 36941, 12650 => 26631,
12651 => 24426, 12652 => 33176, 12653 => 34920, 12654 => 40150, 12655 => 24971,
12656 => 21035, 12657 => 30250, 12658 => 24428, 12659 => 25996, 12660 => 28626,
12661 => 28392, 12662 => 23486, 12663 => 25672, 12664 => 20853, 12665 => 20912,
12666 => 26564, 12667 => 19993, 12668 => 31177, 12669 => 39292, 12670 => 28851,
12833 => 30149, 12834 => 24182, 12835 => 29627, 12836 => 33760, 12837 => 25773,
12838 => 25320, 12839 => 38069, 12840 => 27874, 12841 => 21338, 12842 => 21187,
12843 => 25615, 12844 => 38082, 12845 => 31636, 12846 => 20271, 12847 => 24091,
12848 => 33334, 12849 => 33046, 12850 => 33162, 12851 => 28196, 12852 => 27850,
12853 => 39539, 12854 => 25429, 12855 => 21340, 12856 => 21754, 12857 => 34917,
12858 => 22496, 12859 => 19981, 12860 => 24067, 12861 => 27493, 12862 => 31807,
12863 => 37096, 12864 => 24598, 12865 => 25830, 12866 => 29468, 12867 => 35009,
12868 => 26448, 12869 => 25165, 12870 => 36130, 12871 => 30572, 12872 => 36393,
12873 => 37319, 12874 => 24425, 12875 => 33756, 12876 => 34081, 12877 => 39184,
12878 => 21442, 12879 => 34453, 12880 => 27531, 12881 => 24813, 12882 => 24808,
12883 => 28799, 12884 => 33485, 12885 => 33329, 12886 => 20179, 12887 => 27815,
12888 => 34255, 12889 => 25805, 12890 => 31961, 12891 => 27133, 12892 => 26361,
12893 => 33609, 12894 => 21397, 12895 => 31574, 12896 => 20391, 12897 => 20876,
12898 => 27979, 12899 => 23618, 12900 => 36461, 12901 => 25554, 12902 => 21449,
12903 => 33580, 12904 => 33590, 12905 => 26597, 12906 => 30900, 12907 => 25661,
12908 => 23519, 12909 => 23700, 12910 => 24046, 12911 => 35815, 12912 => 25286,
12913 => 26612, 12914 => 35962, 12915 => 25600, 12916 => 25530, 12917 => 34633,
12918 => 39307, 12919 => 35863, 12920 => 32544, 12921 => 38130, 12922 => 20135,
12923 => 38416, 12924 => 39076, 12925 => 26124, 12926 => 29462, 13089 => 22330,
13090 => 23581, 13091 => 24120, 13092 => 38271, 13093 => 20607, 13094 => 32928,
13095 => 21378, 13096 => 25950, 13097 => 30021, 13098 => 21809, 13099 => 20513,
13100 => 36229, 13101 => 25220, 13102 => 38046, 13103 => 26397, 13104 => 22066,
13105 => 28526, 13106 => 24034, 13107 => 21557, 13108 => 28818, 13109 => 36710,
13110 => 25199, 13111 => 25764, 13112 => 25507, 13113 => 24443, 13114 => 28552,
13115 => 37108, 13116 => 33251, 13117 => 36784, 13118 => 23576, 13119 => 26216,
13120 => 24561, 13121 => 27785, 13122 => 38472, 13123 => 36225, 13124 => 34924,
13125 => 25745, 13126 => 31216, 13127 => 22478, 13128 => 27225, 13129 => 25104,
13130 => 21576, 13131 => 20056, 13132 => 31243, 13133 => 24809, 13134 => 28548,
13135 => 35802, 13136 => 25215, 13137 => 36894, 13138 => 39563, 13139 => 31204,
13140 => 21507, 13141 => 30196, 13142 => 25345, 13143 => 21273, 13144 => 27744,
13145 => 36831, 13146 => 24347, 13147 => 39536, 13148 => 32827, 13149 => 40831,
13150 => 20360, 13151 => 23610, 13152 => 36196, 13153 => 32709, 13154 => 26021,
13155 => 28861, 13156 => 20805, 13157 => 20914, 13158 => 34411, 13159 => 23815,
13160 => 23456, 13161 => 25277, 13162 => 37228, 13163 => 30068, 13164 => 36364,
13165 => 31264, 13166 => 24833, 13167 => 31609, 13168 => 20167, 13169 => 32504,
13170 => 30597, 13171 => 19985, 13172 => 33261, 13173 => 21021, 13174 => 20986,
13175 => 27249, 13176 => 21416, 13177 => 36487, 13178 => 38148, 13179 => 38607,
13180 => 28353, 13181 => 38500, 13182 => 26970, 13345 => 30784, 13346 => 20648,
13347 => 30679, 13348 => 25616, 13349 => 35302, 13350 => 22788, 13351 => 25571,
13352 => 24029, 13353 => 31359, 13354 => 26941, 13355 => 20256, 13356 => 33337,
13357 => 21912, 13358 => 20018, 13359 => 30126, 13360 => 31383, 13361 => 24162,
13362 => 24202, 13363 => 38383, 13364 => 21019, 13365 => 21561, 13366 => 28810,
13367 => 25462, 13368 => 38180, 13369 => 22402, 13370 => 26149, 13371 => 26943,
13372 => 37255, 13373 => 21767, 13374 => 28147, 13375 => 32431, 13376 => 34850,
13377 => 25139, 13378 => 32496, 13379 => 30133, 13380 => 33576, 13381 => 30913,
13382 => 38604, 13383 => 36766, 13384 => 24904, 13385 => 29943, 13386 => 35789,
13387 => 27492, 13388 => 21050, 13389 => 36176, 13390 => 27425, 13391 => 32874,
13392 => 33905, 13393 => 22257, 13394 => 21254, 13395 => 20174, 13396 => 19995,
13397 => 20945, 13398 => 31895, 13399 => 37259, 13400 => 31751, 13401 => 20419,
13402 => 36479, 13403 => 31713, 13404 => 31388, 13405 => 25703, 13406 => 23828,
13407 => 20652, 13408 => 33030, 13409 => 30209, 13410 => 31929, 13411 => 28140,
13412 => 32736, 13413 => 26449, 13414 => 23384, 13415 => 23544, 13416 => 30923,
13417 => 25774, 13418 => 25619, 13419 => 25514, 13420 => 25387, 13421 => 38169,
13422 => 25645, 13423 => 36798, 13424 => 31572, 13425 => 30249, 13426 => 25171,
13427 => 22823, 13428 => 21574, 13429 => 27513, 13430 => 20643, 13431 => 25140,
13432 => 24102, 13433 => 27526, 13434 => 20195, 13435 => 36151, 13436 => 34955,
13437 => 24453, 13438 => 36910, 13601 => 24608, 13602 => 32829, 13603 => 25285,
13604 => 20025, 13605 => 21333, 13606 => 37112, 13607 => 25528, 13608 => 32966,
13609 => 26086, 13610 => 27694, 13611 => 20294, 13612 => 24814, 13613 => 28129,
13614 => 35806, 13615 => 24377, 13616 => 34507, 13617 => 24403, 13618 => 25377,
13619 => 20826, 13620 => 33633, 13621 => 26723, 13622 => 20992, 13623 => 25443,
13624 => 36424, 13625 => 20498, 13626 => 23707, 13627 => 31095, 13628 => 23548,
13629 => 21040, 13630 => 31291, 13631 => 24764, 13632 => 36947, 13633 => 30423,
13634 => 24503, 13635 => 24471, 13636 => 30340, 13637 => 36460, 13638 => 28783,
13639 => 30331, 13640 => 31561, 13641 => 30634, 13642 => 20979, 13643 => 37011,
13644 => 22564, 13645 => 20302, 13646 => 28404, 13647 => 36842, 13648 => 25932,
13649 => 31515, 13650 => 29380, 13651 => 28068, 13652 => 32735, 13653 => 23265,
13654 => 25269, 13655 => 24213, 13656 => 22320, 13657 => 33922, 13658 => 31532,
13659 => 24093, 13660 => 24351, 13661 => 36882, 13662 => 32532, 13663 => 39072,
13664 => 25474, 13665 => 28359, 13666 => 30872, 13667 => 28857, 13668 => 20856,
13669 => 38747, 13670 => 22443, 13671 => 30005, 13672 => 20291, 13673 => 30008,
13674 => 24215, 13675 => 24806, 13676 => 22880, 13677 => 28096, 13678 => 27583,
13679 => 30857, 13680 => 21500, 13681 => 38613, 13682 => 20939, 13683 => 20993,
13684 => 25481, 13685 => 21514, 13686 => 38035, 13687 => 35843, 13688 => 36300,
13689 => 29241, 13690 => 30879, 13691 => 34678, 13692 => 36845, 13693 => 35853,
13694 => 21472, 13857 => 19969, 13858 => 30447, 13859 => 21486, 13860 => 38025,
13861 => 39030, 13862 => 40718, 13863 => 38189, 13864 => 23450, 13865 => 35746,
13866 => 20002, 13867 => 19996, 13868 => 20908, 13869 => 33891, 13870 => 25026,
13871 => 21160, 13872 => 26635, 13873 => 20375, 13874 => 24683, 13875 => 20923,
13876 => 27934, 13877 => 20828, 13878 => 25238, 13879 => 26007, 13880 => 38497,
13881 => 35910, 13882 => 36887, 13883 => 30168, 13884 => 37117, 13885 => 30563,
13886 => 27602, 13887 => 29322, 13888 => 29420, 13889 => 35835, 13890 => 22581,
13891 => 30585, 13892 => 36172, 13893 => 26460, 13894 => 38208, 13895 => 32922,
13896 => 24230, 13897 => 28193, 13898 => 22930, 13899 => 31471, 13900 => 30701,
13901 => 38203, 13902 => 27573, 13903 => 26029, 13904 => 32526, 13905 => 22534,
13906 => 20817, 13907 => 38431, 13908 => 23545, 13909 => 22697, 13910 => 21544,
13911 => 36466, 13912 => 25958, 13913 => 39039, 13914 => 22244, 13915 => 38045,
13916 => 30462, 13917 => 36929, 13918 => 25479, 13919 => 21702, 13920 => 22810,
13921 => 22842, 13922 => 22427, 13923 => 36530, 13924 => 26421, 13925 => 36346,
13926 => 33333, 13927 => 21057, 13928 => 24816, 13929 => 22549, 13930 => 34558,
13931 => 23784, 13932 => 40517, 13933 => 20420, 13934 => 39069, 13935 => 35769,
13936 => 23077, 13937 => 24694, 13938 => 21380, 13939 => 25212, 13940 => 36943,
13941 => 37122, 13942 => 39295, 13943 => 24681, 13944 => 32780, 13945 => 20799,
13946 => 32819, 13947 => 23572, 13948 => 39285, 13949 => 27953, 13950 => 20108,
14113 => 36144, 14114 => 21457, 14115 => 32602, 14116 => 31567, 14117 => 20240,
14118 => 20047, 14119 => 38400, 14120 => 27861, 14121 => 29648, 14122 => 34281,
14123 => 24070, 14124 => 30058, 14125 => 32763, 14126 => 27146, 14127 => 30718,
14128 => 38034, 14129 => 32321, 14130 => 20961, 14131 => 28902, 14132 => 21453,
14133 => 36820, 14134 => 33539, 14135 => 36137, 14136 => 29359, 14137 => 39277,
14138 => 27867, 14139 => 22346, 14140 => 33459, 14141 => 26041, 14142 => 32938,
14143 => 25151, 14144 => 38450, 14145 => 22952, 14146 => 20223, 14147 => 35775,
14148 => 32442, 14149 => 25918, 14150 => 33778, 14151 => 38750, 14152 => 21857,
14153 => 39134, 14154 => 32933, 14155 => 21290, 14156 => 35837, 14157 => 21536,
14158 => 32954, 14159 => 24223, 14160 => 27832, 14161 => 36153, 14162 => 33452,
14163 => 37210, 14164 => 21545, 14165 => 27675, 14166 => 20998, 14167 => 32439,
14168 => 22367, 14169 => 28954, 14170 => 27774, 14171 => 31881, 14172 => 22859,
14173 => 20221, 14174 => 24575, 14175 => 24868, 14176 => 31914, 14177 => 20016,
14178 => 23553, 14179 => 26539, 14180 => 34562, 14181 => 23792, 14182 => 38155,
14183 => 39118, 14184 => 30127, 14185 => 28925, 14186 => 36898, 14187 => 20911,
14188 => 32541, 14189 => 35773, 14190 => 22857, 14191 => 20964, 14192 => 20315,
14193 => 21542, 14194 => 22827, 14195 => 25975, 14196 => 32932, 14197 => 23413,
14198 => 25206, 14199 => 25282, 14200 => 36752, 14201 => 24133, 14202 => 27679,
14203 => 31526, 14204 => 20239, 14205 => 20440, 14206 => 26381, 14369 => 28014,
14370 => 28074, 14371 => 31119, 14372 => 34993, 14373 => 24343, 14374 => 29995,
14375 => 25242, 14376 => 36741, 14377 => 20463, 14378 => 37340, 14379 => 26023,
14380 => 33071, 14381 => 33105, 14382 => 24220, 14383 => 33104, 14384 => 36212,
14385 => 21103, 14386 => 35206, 14387 => 36171, 14388 => 22797, 14389 => 20613,
14390 => 20184, 14391 => 38428, 14392 => 29238, 14393 => 33145, 14394 => 36127,
14395 => 23500, 14396 => 35747, 14397 => 38468, 14398 => 22919, 14399 => 32538,
14400 => 21648, 14401 => 22134, 14402 => 22030, 14403 => 35813, 14404 => 25913,
14405 => 27010, 14406 => 38041, 14407 => 30422, 14408 => 28297, 14409 => 24178,
14410 => 29976, 14411 => 26438, 14412 => 26577, 14413 => 31487, 14414 => 32925,
14415 => 36214, 14416 => 24863, 14417 => 31174, 14418 => 25954, 14419 => 36195,
14420 => 20872, 14421 => 21018, 14422 => 38050, 14423 => 32568, 14424 => 32923,
14425 => 32434, 14426 => 23703, 14427 => 28207, 14428 => 26464, 14429 => 31705,
14430 => 30347, 14431 => 39640, 14432 => 33167, 14433 => 32660, 14434 => 31957,
14435 => 25630, 14436 => 38224, 14437 => 31295, 14438 => 21578, 14439 => 21733,
14440 => 27468, 14441 => 25601, 14442 => 25096, 14443 => 40509, 14444 => 33011,
14445 => 30105, 14446 => 21106, 14447 => 38761, 14448 => 33883, 14449 => 26684,
14450 => 34532, 14451 => 38401, 14452 => 38548, 14453 => 38124, 14454 => 20010,
14455 => 21508, 14456 => 32473, 14457 => 26681, 14458 => 36319, 14459 => 32789,
14460 => 26356, 14461 => 24218, 14462 => 32697, 14625 => 22466, 14626 => 32831,
14627 => 26775, 14628 => 24037, 14629 => 25915, 14630 => 21151, 14631 => 24685,
14632 => 40858, 14633 => 20379, 14634 => 36524, 14635 => 20844, 14636 => 23467,
14637 => 24339, 14638 => 24041, 14639 => 27742, 14640 => 25329, 14641 => 36129,
14642 => 20849, 14643 => 38057, 14644 => 21246, 14645 => 27807, 14646 => 33503,
14647 => 29399, 14648 => 22434, 14649 => 26500, 14650 => 36141, 14651 => 22815,
14652 => 36764, 14653 => 33735, 14654 => 21653, 14655 => 31629, 14656 => 20272,
14657 => 27837, 14658 => 23396, 14659 => 22993, 14660 => 40723, 14661 => 21476,
14662 => 34506, 14663 => 39592, 14664 => 35895, 14665 => 32929, 14666 => 25925,
14667 => 39038, 14668 => 22266, 14669 => 38599, 14670 => 21038, 14671 => 29916,
14672 => 21072, 14673 => 23521, 14674 => 25346, 14675 => 35074, 14676 => 20054,
14677 => 25296, 14678 => 24618, 14679 => 26874, 14680 => 20851, 14681 => 23448,
14682 => 20896, 14683 => 35266, 14684 => 31649, 14685 => 39302, 14686 => 32592,
14687 => 24815, 14688 => 28748, 14689 => 36143, 14690 => 20809, 14691 => 24191,
14692 => 36891, 14693 => 29808, 14694 => 35268, 14695 => 22317, 14696 => 30789,
14697 => 24402, 14698 => 40863, 14699 => 38394, 14700 => 36712, 14701 => 39740,
14702 => 35809, 14703 => 30328, 14704 => 26690, 14705 => 26588, 14706 => 36330,
14707 => 36149, 14708 => 21053, 14709 => 36746, 14710 => 28378, 14711 => 26829,
14712 => 38149, 14713 => 37101, 14714 => 22269, 14715 => 26524, 14716 => 35065,
14717 => 36807, 14718 => 21704, 14881 => 39608, 14882 => 23401, 14883 => 28023,
14884 => 27686, 14885 => 20133, 14886 => 23475, 14887 => 39559, 14888 => 37219,
14889 => 25000, 14890 => 37039, 14891 => 38889, 14892 => 21547, 14893 => 28085,
14894 => 23506, 14895 => 20989, 14896 => 21898, 14897 => 32597, 14898 => 32752,
14899 => 25788, 14900 => 25421, 14901 => 26097, 14902 => 25022, 14903 => 24717,
14904 => 28938, 14905 => 27735, 14906 => 27721, 14907 => 22831, 14908 => 26477,
14909 => 33322, 14910 => 22741, 14911 => 22158, 14912 => 35946, 14913 => 27627,
14914 => 37085, 14915 => 22909, 14916 => 32791, 14917 => 21495, 14918 => 28009,
14919 => 21621, 14920 => 21917, 14921 => 33655, 14922 => 33743, 14923 => 26680,
14924 => 31166, 14925 => 21644, 14926 => 20309, 14927 => 21512, 14928 => 30418,
14929 => 35977, 14930 => 38402, 14931 => 27827, 14932 => 28088, 14933 => 36203,
14934 => 35088, 14935 => 40548, 14936 => 36154, 14937 => 22079, 14938 => 40657,
14939 => 30165, 14940 => 24456, 14941 => 29408, 14942 => 24680, 14943 => 21756,
14944 => 20136, 14945 => 27178, 14946 => 34913, 14947 => 24658, 14948 => 36720,
14949 => 21700, 14950 => 28888, 14951 => 34425, 14952 => 40511, 14953 => 27946,
14954 => 23439, 14955 => 24344, 14956 => 32418, 14957 => 21897, 14958 => 20399,
14959 => 29492, 14960 => 21564, 14961 => 21402, 14962 => 20505, 14963 => 21518,
14964 => 21628, 14965 => 20046, 14966 => 24573, 14967 => 29786, 14968 => 22774,
14969 => 33899, 14970 => 32993, 14971 => 34676, 14972 => 29392, 14973 => 31946,
14974 => 28246, 15137 => 24359, 15138 => 34382, 15139 => 21804, 15140 => 25252,
15141 => 20114, 15142 => 27818, 15143 => 25143, 15144 => 33457, 15145 => 21719,
15146 => 21326, 15147 => 29502, 15148 => 28369, 15149 => 30011, 15150 => 21010,
15151 => 21270, 15152 => 35805, 15153 => 27088, 15154 => 24458, 15155 => 24576,
15156 => 28142, 15157 => 22351, 15158 => 27426, 15159 => 29615, 15160 => 26707,
15161 => 36824, 15162 => 32531, 15163 => 25442, 15164 => 24739, 15165 => 21796,
15166 => 30186, 15167 => 35938, 15168 => 28949, 15169 => 28067, 15170 => 23462,
15171 => 24187, 15172 => 33618, 15173 => 24908, 15174 => 40644, 15175 => 30970,
15176 => 34647, 15177 => 31783, 15178 => 30343, 15179 => 20976, 15180 => 24822,
15181 => 29004, 15182 => 26179, 15183 => 24140, 15184 => 24653, 15185 => 35854,
15186 => 28784, 15187 => 25381, 15188 => 36745, 15189 => 24509, 15190 => 24674,
15191 => 34516, 15192 => 22238, 15193 => 27585, 15194 => 24724, 15195 => 24935,
15196 => 21321, 15197 => 24800, 15198 => 26214, 15199 => 36159, 15200 => 31229,
15201 => 20250, 15202 => 28905, 15203 => 27719, 15204 => 35763, 15205 => 35826,
15206 => 32472, 15207 => 33636, 15208 => 26127, 15209 => 23130, 15210 => 39746,
15211 => 27985, 15212 => 28151, 15213 => 35905, 15214 => 27963, 15215 => 20249,
15216 => 28779, 15217 => 33719, 15218 => 25110, 15219 => 24785, 15220 => 38669,
15221 => 36135, 15222 => 31096, 15223 => 20987, 15224 => 22334, 15225 => 22522,
15226 => 26426, 15227 => 30072, 15228 => 31293, 15229 => 31215, 15230 => 31637,
15393 => 32908, 15394 => 39269, 15395 => 36857, 15396 => 28608, 15397 => 35749,
15398 => 40481, 15399 => 23020, 15400 => 32489, 15401 => 32521, 15402 => 21513,
15403 => 26497, 15404 => 26840, 15405 => 36753, 15406 => 31821, 15407 => 38598,
15408 => 21450, 15409 => 24613, 15410 => 30142, 15411 => 27762, 15412 => 21363,
15413 => 23241, 15414 => 32423, 15415 => 25380, 15416 => 20960, 15417 => 33034,
15418 => 24049, 15419 => 34015, 15420 => 25216, 15421 => 20864, 15422 => 23395,
15423 => 20238, 15424 => 31085, 15425 => 21058, 15426 => 24760, 15427 => 27982,
15428 => 23492, 15429 => 23490, 15430 => 35745, 15431 => 35760, 15432 => 26082,
15433 => 24524, 15434 => 38469, 15435 => 22931, 15436 => 32487, 15437 => 32426,
15438 => 22025, 15439 => 26551, 15440 => 22841, 15441 => 20339, 15442 => 23478,
15443 => 21152, 15444 => 33626, 15445 => 39050, 15446 => 36158, 15447 => 30002,
15448 => 38078, 15449 => 20551, 15450 => 31292, 15451 => 20215, 15452 => 26550,
15453 => 39550, 15454 => 23233, 15455 => 27516, 15456 => 30417, 15457 => 22362,
15458 => 23574, 15459 => 31546, 15460 => 38388, 15461 => 29006, 15462 => 20860,
15463 => 32937, 15464 => 33392, 15465 => 22904, 15466 => 32516, 15467 => 33575,
15468 => 26816, 15469 => 26604, 15470 => 30897, 15471 => 30839, 15472 => 25315,
15473 => 25441, 15474 => 31616, 15475 => 20461, 15476 => 21098, 15477 => 20943,
15478 => 33616, 15479 => 27099, 15480 => 37492, 15481 => 36341, 15482 => 36145,
15483 => 35265, 15484 => 38190, 15485 => 31661, 15486 => 20214, 15649 => 20581,
15650 => 33328, 15651 => 21073, 15652 => 39279, 15653 => 28176, 15654 => 28293,
15655 => 28071, 15656 => 24314, 15657 => 20725, 15658 => 23004, 15659 => 23558,
15660 => 27974, 15661 => 27743, 15662 => 30086, 15663 => 33931, 15664 => 26728,
15665 => 22870, 15666 => 35762, 15667 => 21280, 15668 => 37233, 15669 => 38477,
15670 => 34121, 15671 => 26898, 15672 => 30977, 15673 => 28966, 15674 => 33014,
15675 => 20132, 15676 => 37066, 15677 => 27975, 15678 => 39556, 15679 => 23047,
15680 => 22204, 15681 => 25605, 15682 => 38128, 15683 => 30699, 15684 => 20389,
15685 => 33050, 15686 => 29409, 15687 => 35282, 15688 => 39290, 15689 => 32564,
15690 => 32478, 15691 => 21119, 15692 => 25945, 15693 => 37237, 15694 => 36735,
15695 => 36739, 15696 => 21483, 15697 => 31382, 15698 => 25581, 15699 => 25509,
15700 => 30342, 15701 => 31224, 15702 => 34903, 15703 => 38454, 15704 => 25130,
15705 => 21163, 15706 => 33410, 15707 => 26708, 15708 => 26480, 15709 => 25463,
15710 => 30571, 15711 => 31469, 15712 => 27905, 15713 => 32467, 15714 => 35299,
15715 => 22992, 15716 => 25106, 15717 => 34249, 15718 => 33445, 15719 => 30028,
15720 => 20511, 15721 => 20171, 15722 => 30117, 15723 => 35819, 15724 => 23626,
15725 => 24062, 15726 => 31563, 15727 => 26020, 15728 => 37329, 15729 => 20170,
15730 => 27941, 15731 => 35167, 15732 => 32039, 15733 => 38182, 15734 => 20165,
15735 => 35880, 15736 => 36827, 15737 => 38771, 15738 => 26187, 15739 => 31105,
15740 => 36817, 15741 => 28908, 15742 => 28024, 15905 => 23613, 15906 => 21170,
15907 => 33606, 15908 => 20834, 15909 => 33550, 15910 => 30555, 15911 => 26230,
15912 => 40120, 15913 => 20140, 15914 => 24778, 15915 => 31934, 15916 => 31923,
15917 => 32463, 15918 => 20117, 15919 => 35686, 15920 => 26223, 15921 => 39048,
15922 => 38745, 15923 => 22659, 15924 => 25964, 15925 => 38236, 15926 => 24452,
15927 => 30153, 15928 => 38742, 15929 => 31455, 15930 => 31454, 15931 => 20928,
15932 => 28847, 15933 => 31384, 15934 => 25578, 15935 => 31350, 15936 => 32416,
15937 => 29590, 15938 => 38893, 15939 => 20037, 15940 => 28792, 15941 => 20061,
15942 => 37202, 15943 => 21417, 15944 => 25937, 15945 => 26087, 15946 => 33276,
15947 => 33285, 15948 => 21646, 15949 => 23601, 15950 => 30106, 15951 => 38816,
15952 => 25304, 15953 => 29401, 15954 => 30141, 15955 => 23621, 15956 => 39545,
15957 => 33738, 15958 => 23616, 15959 => 21632, 15960 => 30697, 15961 => 20030,
15962 => 27822, 15963 => 32858, 15964 => 25298, 15965 => 25454, 15966 => 24040,
15967 => 20855, 15968 => 36317, 15969 => 36382, 15970 => 38191, 15971 => 20465,
15972 => 21477, 15973 => 24807, 15974 => 28844, 15975 => 21095, 15976 => 25424,
15977 => 40515, 15978 => 23071, 15979 => 20518, 15980 => 30519, 15981 => 21367,
15982 => 32482, 15983 => 25733, 15984 => 25899, 15985 => 25225, 15986 => 25496,
15987 => 20500, 15988 => 29237, 15989 => 35273, 15990 => 20915, 15991 => 35776,
15992 => 32477, 15993 => 22343, 15994 => 33740, 15995 => 38055, 15996 => 20891,
15997 => 21531, 15998 => 23803, 16161 => 20426, 16162 => 31459, 16163 => 27994,
16164 => 37089, 16165 => 39567, 16166 => 21888, 16167 => 21654, 16168 => 21345,
16169 => 21679, 16170 => 24320, 16171 => 25577, 16172 => 26999, 16173 => 20975,
16174 => 24936, 16175 => 21002, 16176 => 22570, 16177 => 21208, 16178 => 22350,
16179 => 30733, 16180 => 30475, 16181 => 24247, 16182 => 24951, 16183 => 31968,
16184 => 25179, 16185 => 25239, 16186 => 20130, 16187 => 28821, 16188 => 32771,
16189 => 25335, 16190 => 28900, 16191 => 38752, 16192 => 22391, 16193 => 33499,
16194 => 26607, 16195 => 26869, 16196 => 30933, 16197 => 39063, 16198 => 31185,
16199 => 22771, 16200 => 21683, 16201 => 21487, 16202 => 28212, 16203 => 20811,
16204 => 21051, 16205 => 23458, 16206 => 35838, 16207 => 32943, 16208 => 21827,
16209 => 22438, 16210 => 24691, 16211 => 22353, 16212 => 21549, 16213 => 31354,
16214 => 24656, 16215 => 23380, 16216 => 25511, 16217 => 25248, 16218 => 21475,
16219 => 25187, 16220 => 23495, 16221 => 26543, 16222 => 21741, 16223 => 31391,
16224 => 33510, 16225 => 37239, 16226 => 24211, 16227 => 35044, 16228 => 22840,
16229 => 22446, 16230 => 25358, 16231 => 36328, 16232 => 33007, 16233 => 22359,
16234 => 31607, 16235 => 20393, 16236 => 24555, 16237 => 23485, 16238 => 27454,
16239 => 21281, 16240 => 31568, 16241 => 29378, 16242 => 26694, 16243 => 30719,
16244 => 30518, 16245 => 26103, 16246 => 20917, 16247 => 20111, 16248 => 30420,
16249 => 23743, 16250 => 31397, 16251 => 33909, 16252 => 22862, 16253 => 39745,
16254 => 20608, 16417 => 39304, 16418 => 24871, 16419 => 28291, 16420 => 22372,
16421 => 26118, 16422 => 25414, 16423 => 22256, 16424 => 25324, 16425 => 25193,
16426 => 24275, 16427 => 38420, 16428 => 22403, 16429 => 25289, 16430 => 21895,
16431 => 34593, 16432 => 33098, 16433 => 36771, 16434 => 21862, 16435 => 33713,
16436 => 26469, 16437 => 36182, 16438 => 34013, 16439 => 23146, 16440 => 26639,
16441 => 25318, 16442 => 31726, 16443 => 38417, 16444 => 20848, 16445 => 28572,
16446 => 35888, 16447 => 25597, 16448 => 35272, 16449 => 25042, 16450 => 32518,
16451 => 28866, 16452 => 28389, 16453 => 29701, 16454 => 27028, 16455 => 29436,
16456 => 24266, 16457 => 37070, 16458 => 26391, 16459 => 28010, 16460 => 25438,
16461 => 21171, 16462 => 29282, 16463 => 32769, 16464 => 20332, 16465 => 23013,
16466 => 37226, 16467 => 28889, 16468 => 28061, 16469 => 21202, 16470 => 20048,
16471 => 38647, 16472 => 38253, 16473 => 34174, 16474 => 30922, 16475 => 32047,
16476 => 20769, 16477 => 22418, 16478 => 25794, 16479 => 32907, 16480 => 31867,
16481 => 27882, 16482 => 26865, 16483 => 26974, 16484 => 20919, 16485 => 21400,
16486 => 26792, 16487 => 29313, 16488 => 40654, 16489 => 31729, 16490 => 29432,
16491 => 31163, 16492 => 28435, 16493 => 29702, 16494 => 26446, 16495 => 37324,
16496 => 40100, 16497 => 31036, 16498 => 33673, 16499 => 33620, 16500 => 21519,
16501 => 26647, 16502 => 20029, 16503 => 21385, 16504 => 21169, 16505 => 30782,
16506 => 21382, 16507 => 21033, 16508 => 20616, 16509 => 20363, 16510 => 20432,
16673 => 30178, 16674 => 31435, 16675 => 31890, 16676 => 27813, 16677 => 38582,
16678 => 21147, 16679 => 29827, 16680 => 21737, 16681 => 20457, 16682 => 32852,
16683 => 33714, 16684 => 36830, 16685 => 38256, 16686 => 24265, 16687 => 24604,
16688 => 28063, 16689 => 24088, 16690 => 25947, 16691 => 33080, 16692 => 38142,
16693 => 24651, 16694 => 28860, 16695 => 32451, 16696 => 31918, 16697 => 20937,
16698 => 26753, 16699 => 31921, 16700 => 33391, 16701 => 20004, 16702 => 36742,
16703 => 37327, 16704 => 26238, 16705 => 20142, 16706 => 35845, 16707 => 25769,
16708 => 32842, 16709 => 20698, 16710 => 30103, 16711 => 29134, 16712 => 23525,
16713 => 36797, 16714 => 28518, 16715 => 20102, 16716 => 25730, 16717 => 38243,
16718 => 24278, 16719 => 26009, 16720 => 21015, 16721 => 35010, 16722 => 28872,
16723 => 21155, 16724 => 29454, 16725 => 29747, 16726 => 26519, 16727 => 30967,
16728 => 38678, 16729 => 20020, 16730 => 37051, 16731 => 40158, 16732 => 28107,
16733 => 20955, 16734 => 36161, 16735 => 21533, 16736 => 25294, 16737 => 29618,
16738 => 33777, 16739 => 38646, 16740 => 40836, 16741 => 38083, 16742 => 20278,
16743 => 32666, 16744 => 20940, 16745 => 28789, 16746 => 38517, 16747 => 23725,
16748 => 39046, 16749 => 21478, 16750 => 20196, 16751 => 28316, 16752 => 29705,
16753 => 27060, 16754 => 30827, 16755 => 39311, 16756 => 30041, 16757 => 21016,
16758 => 30244, 16759 => 27969, 16760 => 26611, 16761 => 20845, 16762 => 40857,
16763 => 32843, 16764 => 21657, 16765 => 31548, 16766 => 31423, 16929 => 38534,
16930 => 22404, 16931 => 25314, 16932 => 38471, 16933 => 27004, 16934 => 23044,
16935 => 25602, 16936 => 31699, 16937 => 28431, 16938 => 38475, 16939 => 33446,
16940 => 21346, 16941 => 39045, 16942 => 24208, 16943 => 28809, 16944 => 25523,
16945 => 21348, 16946 => 34383, 16947 => 40065, 16948 => 40595, 16949 => 30860,
16950 => 38706, 16951 => 36335, 16952 => 36162, 16953 => 40575, 16954 => 28510,
16955 => 31108, 16956 => 24405, 16957 => 38470, 16958 => 25134, 16959 => 39540,
16960 => 21525, 16961 => 38109, 16962 => 20387, 16963 => 26053, 16964 => 23653,
16965 => 23649, 16966 => 32533, 16967 => 34385, 16968 => 27695, 16969 => 24459,
16970 => 29575, 16971 => 28388, 16972 => 32511, 16973 => 23782, 16974 => 25371,
16975 => 23402, 16976 => 28390, 16977 => 21365, 16978 => 20081, 16979 => 25504,
16980 => 30053, 16981 => 25249, 16982 => 36718, 16983 => 20262, 16984 => 20177,
16985 => 27814, 16986 => 32438, 16987 => 35770, 16988 => 33821, 16989 => 34746,
16990 => 32599, 16991 => 36923, 16992 => 38179, 16993 => 31657, 16994 => 39585,
16995 => 35064, 16996 => 33853, 16997 => 27931, 16998 => 39558, 16999 => 32476,
17000 => 22920, 17001 => 40635, 17002 => 29595, 17003 => 30721, 17004 => 34434,
17005 => 39532, 17006 => 39554, 17007 => 22043, 17008 => 21527, 17009 => 22475,
17010 => 20080, 17011 => 40614, 17012 => 21334, 17013 => 36808, 17014 => 33033,
17015 => 30610, 17016 => 39314, 17017 => 34542, 17018 => 28385, 17019 => 34067,
17020 => 26364, 17021 => 24930, 17022 => 28459, 17185 => 35881, 17186 => 33426,
17187 => 33579, 17188 => 30450, 17189 => 27667, 17190 => 24537, 17191 => 33725,
17192 => 29483, 17193 => 33541, 17194 => 38170, 17195 => 27611, 17196 => 30683,
17197 => 38086, 17198 => 21359, 17199 => 33538, 17200 => 20882, 17201 => 24125,
17202 => 35980, 17203 => 36152, 17204 => 20040, 17205 => 29611, 17206 => 26522,
17207 => 26757, 17208 => 37238, 17209 => 38665, 17210 => 29028, 17211 => 27809,
17212 => 30473, 17213 => 23186, 17214 => 38209, 17215 => 27599, 17216 => 32654,
17217 => 26151, 17218 => 23504, 17219 => 22969, 17220 => 23194, 17221 => 38376,
17222 => 38391, 17223 => 20204, 17224 => 33804, 17225 => 33945, 17226 => 27308,
17227 => 30431, 17228 => 38192, 17229 => 29467, 17230 => 26790, 17231 => 23391,
17232 => 30511, 17233 => 37274, 17234 => 38753, 17235 => 31964, 17236 => 36855,
17237 => 35868, 17238 => 24357, 17239 => 31859, 17240 => 31192, 17241 => 35269,
17242 => 27852, 17243 => 34588, 17244 => 23494, 17245 => 24130, 17246 => 26825,
17247 => 30496, 17248 => 32501, 17249 => 20885, 17250 => 20813, 17251 => 21193,
17252 => 23081, 17253 => 32517, 17254 => 38754, 17255 => 33495, 17256 => 25551,
17257 => 30596, 17258 => 34256, 17259 => 31186, 17260 => 28218, 17261 => 24217,
17262 => 22937, 17263 => 34065, 17264 => 28781, 17265 => 27665, 17266 => 25279,
17267 => 30399, 17268 => 25935, 17269 => 24751, 17270 => 38397, 17271 => 26126,
17272 => 34719, 17273 => 40483, 17274 => 38125, 17275 => 21517, 17276 => 21629,
17277 => 35884, 17278 => 25720, 17441 => 25721, 17442 => 34321, 17443 => 27169,
17444 => 33180, 17445 => 30952, 17446 => 25705, 17447 => 39764, 17448 => 25273,
17449 => 26411, 17450 => 33707, 17451 => 22696, 17452 => 40664, 17453 => 27819,
17454 => 28448, 17455 => 23518, 17456 => 38476, 17457 => 35851, 17458 => 29279,
17459 => 26576, 17460 => 25287, 17461 => 29281, 17462 => 20137, 17463 => 22982,
17464 => 27597, 17465 => 22675, 17466 => 26286, 17467 => 24149, 17468 => 21215,
17469 => 24917, 17470 => 26408, 17471 => 30446, 17472 => 30566, 17473 => 29287,
17474 => 31302, 17475 => 25343, 17476 => 21738, 17477 => 21584, 17478 => 38048,
17479 => 37027, 17480 => 23068, 17481 => 32435, 17482 => 27670, 17483 => 20035,
17484 => 22902, 17485 => 32784, 17486 => 22856, 17487 => 21335, 17488 => 30007,
17489 => 38590, 17490 => 22218, 17491 => 25376, 17492 => 33041, 17493 => 24700,
17494 => 38393, 17495 => 28118, 17496 => 21602, 17497 => 39297, 17498 => 20869,
17499 => 23273, 17500 => 33021, 17501 => 22958, 17502 => 38675, 17503 => 20522,
17504 => 27877, 17505 => 23612, 17506 => 25311, 17507 => 20320, 17508 => 21311,
17509 => 33147, 17510 => 36870, 17511 => 28346, 17512 => 34091, 17513 => 25288,
17514 => 24180, 17515 => 30910, 17516 => 25781, 17517 => 25467, 17518 => 24565,
17519 => 23064, 17520 => 37247, 17521 => 40479, 17522 => 23615, 17523 => 25423,
17524 => 32834, 17525 => 23421, 17526 => 21870, 17527 => 38218, 17528 => 38221,
17529 => 28037, 17530 => 24744, 17531 => 26592, 17532 => 29406, 17533 => 20957,
17534 => 23425, 17697 => 25319, 17698 => 27870, 17699 => 29275, 17700 => 25197,
17701 => 38062, 17702 => 32445, 17703 => 33043, 17704 => 27987, 17705 => 20892,
17706 => 24324, 17707 => 22900, 17708 => 21162, 17709 => 24594, 17710 => 22899,
17711 => 26262, 17712 => 34384, 17713 => 30111, 17714 => 25386, 17715 => 25062,
17716 => 31983, 17717 => 35834, 17718 => 21734, 17719 => 27431, 17720 => 40485,
17721 => 27572, 17722 => 34261, 17723 => 21589, 17724 => 20598, 17725 => 27812,
17726 => 21866, 17727 => 36276, 17728 => 29228, 17729 => 24085, 17730 => 24597,
17731 => 29750, 17732 => 25293, 17733 => 25490, 17734 => 29260, 17735 => 24472,
17736 => 28227, 17737 => 27966, 17738 => 25856, 17739 => 28504, 17740 => 30424,
17741 => 30928, 17742 => 30460, 17743 => 30036, 17744 => 21028, 17745 => 21467,
17746 => 20051, 17747 => 24222, 17748 => 26049, 17749 => 32810, 17750 => 32982,
17751 => 25243, 17752 => 21638, 17753 => 21032, 17754 => 28846, 17755 => 34957,
17756 => 36305, 17757 => 27873, 17758 => 21624, 17759 => 32986, 17760 => 22521,
17761 => 35060, 17762 => 36180, 17763 => 38506, 17764 => 37197, 17765 => 20329,
17766 => 27803, 17767 => 21943, 17768 => 30406, 17769 => 30768, 17770 => 25256,
17771 => 28921, 17772 => 28558, 17773 => 24429, 17774 => 34028, 17775 => 26842,
17776 => 30844, 17777 => 31735, 17778 => 33192, 17779 => 26379, 17780 => 40527,
17781 => 25447, 17782 => 30896, 17783 => 22383, 17784 => 30738, 17785 => 38713,
17786 => 25209, 17787 => 25259, 17788 => 21128, 17789 => 29749, 17790 => 27607,
17953 => 21860, 17954 => 33086, 17955 => 30130, 17956 => 30382, 17957 => 21305,
17958 => 30174, 17959 => 20731, 17960 => 23617, 17961 => 35692, 17962 => 31687,
17963 => 20559, 17964 => 29255, 17965 => 39575, 17966 => 39128, 17967 => 28418,
17968 => 29922, 17969 => 31080, 17970 => 25735, 17971 => 30629, 17972 => 25340,
17973 => 39057, 17974 => 36139, 17975 => 21697, 17976 => 32856, 17977 => 20050,
17978 => 22378, 17979 => 33529, 17980 => 33805, 17981 => 24179, 17982 => 20973,
17983 => 29942, 17984 => 35780, 17985 => 23631, 17986 => 22369, 17987 => 27900,
17988 => 39047, 17989 => 23110, 17990 => 30772, 17991 => 39748, 17992 => 36843,
17993 => 31893, 17994 => 21078, 17995 => 25169, 17996 => 38138, 17997 => 20166,
17998 => 33670, 17999 => 33889, 18000 => 33769, 18001 => 33970, 18002 => 22484,
18003 => 26420, 18004 => 22275, 18005 => 26222, 18006 => 28006, 18007 => 35889,
18008 => 26333, 18009 => 28689, 18010 => 26399, 18011 => 27450, 18012 => 26646,
18013 => 25114, 18014 => 22971, 18015 => 19971, 18016 => 20932, 18017 => 28422,
18018 => 26578, 18019 => 27791, 18020 => 20854, 18021 => 26827, 18022 => 22855,
18023 => 27495, 18024 => 30054, 18025 => 23822, 18026 => 33040, 18027 => 40784,
18028 => 26071, 18029 => 31048, 18030 => 31041, 18031 => 39569, 18032 => 36215,
18033 => 23682, 18034 => 20062, 18035 => 20225, 18036 => 21551, 18037 => 22865,
18038 => 30732, 18039 => 22120, 18040 => 27668, 18041 => 36804, 18042 => 24323,
18043 => 27773, 18044 => 27875, 18045 => 35755, 18046 => 25488, 18209 => 24688,
18210 => 27965, 18211 => 29301, 18212 => 25190, 18213 => 38030, 18214 => 38085,
18215 => 21315, 18216 => 36801, 18217 => 31614, 18218 => 20191, 18219 => 35878,
18220 => 20094, 18221 => 40660, 18222 => 38065, 18223 => 38067, 18224 => 21069,
18225 => 28508, 18226 => 36963, 18227 => 27973, 18228 => 35892, 18229 => 22545,
18230 => 23884, 18231 => 27424, 18232 => 27465, 18233 => 26538, 18234 => 21595,
18235 => 33108, 18236 => 32652, 18237 => 22681, 18238 => 34103, 18239 => 24378,
18240 => 25250, 18241 => 27207, 18242 => 38201, 18243 => 25970, 18244 => 24708,
18245 => 26725, 18246 => 30631, 18247 => 20052, 18248 => 20392, 18249 => 24039,
18250 => 38808, 18251 => 25772, 18252 => 32728, 18253 => 23789, 18254 => 20431,
18255 => 31373, 18256 => 20999, 18257 => 33540, 18258 => 19988, 18259 => 24623,
18260 => 31363, 18261 => 38054, 18262 => 20405, 18263 => 20146, 18264 => 31206,
18265 => 29748, 18266 => 21220, 18267 => 33465, 18268 => 25810, 18269 => 31165,
18270 => 23517, 18271 => 27777, 18272 => 38738, 18273 => 36731, 18274 => 27682,
18275 => 20542, 18276 => 21375, 18277 => 28165, 18278 => 25806, 18279 => 26228,
18280 => 27696, 18281 => 24773, 18282 => 39031, 18283 => 35831, 18284 => 24198,
18285 => 29756, 18286 => 31351, 18287 => 31179, 18288 => 19992, 18289 => 37041,
18290 => 29699, 18291 => 27714, 18292 => 22234, 18293 => 37195, 18294 => 27845,
18295 => 36235, 18296 => 21306, 18297 => 34502, 18298 => 26354, 18299 => 36527,
18300 => 23624, 18301 => 39537, 18302 => 28192, 18465 => 21462, 18466 => 23094,
18467 => 40843, 18468 => 36259, 18469 => 21435, 18470 => 22280, 18471 => 39079,
18472 => 26435, 18473 => 37275, 18474 => 27849, 18475 => 20840, 18476 => 30154,
18477 => 25331, 18478 => 29356, 18479 => 21048, 18480 => 21149, 18481 => 32570,
18482 => 28820, 18483 => 30264, 18484 => 21364, 18485 => 40522, 18486 => 27063,
18487 => 30830, 18488 => 38592, 18489 => 35033, 18490 => 32676, 18491 => 28982,
18492 => 29123, 18493 => 20873, 18494 => 26579, 18495 => 29924, 18496 => 22756,
18497 => 25880, 18498 => 22199, 18499 => 35753, 18500 => 39286, 18501 => 25200,
18502 => 32469, 18503 => 24825, 18504 => 28909, 18505 => 22764, 18506 => 20161,
18507 => 20154, 18508 => 24525, 18509 => 38887, 18510 => 20219, 18511 => 35748,
18512 => 20995, 18513 => 22922, 18514 => 32427, 18515 => 25172, 18516 => 20173,
18517 => 26085, 18518 => 25102, 18519 => 33592, 18520 => 33993, 18521 => 33635,
18522 => 34701, 18523 => 29076, 18524 => 28342, 18525 => 23481, 18526 => 32466,
18527 => 20887, 18528 => 25545, 18529 => 26580, 18530 => 32905, 18531 => 33593,
18532 => 34837, 18533 => 20754, 18534 => 23418, 18535 => 22914, 18536 => 36785,
18537 => 20083, 18538 => 27741, 18539 => 20837, 18540 => 35109, 18541 => 36719,
18542 => 38446, 18543 => 34122, 18544 => 29790, 18545 => 38160, 18546 => 38384,
18547 => 28070, 18548 => 33509, 18549 => 24369, 18550 => 25746, 18551 => 27922,
18552 => 33832, 18553 => 33134, 18554 => 40131, 18555 => 22622, 18556 => 36187,
18557 => 19977, 18558 => 21441, 18721 => 20254, 18722 => 25955, 18723 => 26705,
18724 => 21971, 18725 => 20007, 18726 => 25620, 18727 => 39578, 18728 => 25195,
18729 => 23234, 18730 => 29791, 18731 => 33394, 18732 => 28073, 18733 => 26862,
18734 => 20711, 18735 => 33678, 18736 => 30722, 18737 => 26432, 18738 => 21049,
18739 => 27801, 18740 => 32433, 18741 => 20667, 18742 => 21861, 18743 => 29022,
18744 => 31579, 18745 => 26194, 18746 => 29642, 18747 => 33515, 18748 => 26441,
18749 => 23665, 18750 => 21024, 18751 => 29053, 18752 => 34923, 18753 => 38378,
18754 => 38485, 18755 => 25797, 18756 => 36193, 18757 => 33203, 18758 => 21892,
18759 => 27733, 18760 => 25159, 18761 => 32558, 18762 => 22674, 18763 => 20260,
18764 => 21830, 18765 => 36175, 18766 => 26188, 18767 => 19978, 18768 => 23578,
18769 => 35059, 18770 => 26786, 18771 => 25422, 18772 => 31245, 18773 => 28903,
18774 => 33421, 18775 => 21242, 18776 => 38902, 18777 => 23569, 18778 => 21736,
18779 => 37045, 18780 => 32461, 18781 => 22882, 18782 => 36170, 18783 => 34503,
18784 => 33292, 18785 => 33293, 18786 => 36198, 18787 => 25668, 18788 => 23556,
18789 => 24913, 18790 => 28041, 18791 => 31038, 18792 => 35774, 18793 => 30775,
18794 => 30003, 18795 => 21627, 18796 => 20280, 18797 => 36523, 18798 => 28145,
18799 => 23072, 18800 => 32453, 18801 => 31070, 18802 => 27784, 18803 => 23457,
18804 => 23158, 18805 => 29978, 18806 => 32958, 18807 => 24910, 18808 => 28183,
18809 => 22768, 18810 => 29983, 18811 => 29989, 18812 => 29298, 18813 => 21319,
18814 => 32499, 18977 => 30465, 18978 => 30427, 18979 => 21097, 18980 => 32988,
18981 => 22307, 18982 => 24072, 18983 => 22833, 18984 => 29422, 18985 => 26045,
18986 => 28287, 18987 => 35799, 18988 => 23608, 18989 => 34417, 18990 => 21313,
18991 => 30707, 18992 => 25342, 18993 => 26102, 18994 => 20160, 18995 => 39135,
18996 => 34432, 18997 => 23454, 18998 => 35782, 18999 => 21490, 19000 => 30690,
19001 => 20351, 19002 => 23630, 19003 => 39542, 19004 => 22987, 19005 => 24335,
19006 => 31034, 19007 => 22763, 19008 => 19990, 19009 => 26623, 19010 => 20107,
19011 => 25325, 19012 => 35475, 19013 => 36893, 19014 => 21183, 19015 => 26159,
19016 => 21980, 19017 => 22124, 19018 => 36866, 19019 => 20181, 19020 => 20365,
19021 => 37322, 19022 => 39280, 19023 => 27663, 19024 => 24066, 19025 => 24643,
19026 => 23460, 19027 => 35270, 19028 => 35797, 19029 => 25910, 19030 => 25163,
19031 => 39318, 19032 => 23432, 19033 => 23551, 19034 => 25480, 19035 => 21806,
19036 => 21463, 19037 => 30246, 19038 => 20861, 19039 => 34092, 19040 => 26530,
19041 => 26803, 19042 => 27530, 19043 => 25234, 19044 => 36755, 19045 => 21460,
19046 => 33298, 19047 => 28113, 19048 => 30095, 19049 => 20070, 19050 => 36174,
19051 => 23408, 19052 => 29087, 19053 => 34223, 19054 => 26257, 19055 => 26329,
19056 => 32626, 19057 => 34560, 19058 => 40653, 19059 => 40736, 19060 => 23646,
19061 => 26415, 19062 => 36848, 19063 => 26641, 19064 => 26463, 19065 => 25101,
19066 => 31446, 19067 => 22661, 19068 => 24246, 19069 => 25968, 19070 => 28465,
19233 => 24661, 19234 => 21047, 19235 => 32781, 19236 => 25684, 19237 => 34928,
19238 => 29993, 19239 => 24069, 19240 => 26643, 19241 => 25332, 19242 => 38684,
19243 => 21452, 19244 => 29245, 19245 => 35841, 19246 => 27700, 19247 => 30561,
19248 => 31246, 19249 => 21550, 19250 => 30636, 19251 => 39034, 19252 => 33308,
19253 => 35828, 19254 => 30805, 19255 => 26388, 19256 => 28865, 19257 => 26031,
19258 => 25749, 19259 => 22070, 19260 => 24605, 19261 => 31169, 19262 => 21496,
19263 => 19997, 19264 => 27515, 19265 => 32902, 19266 => 23546, 19267 => 21987,
19268 => 22235, 19269 => 20282, 19270 => 20284, 19271 => 39282, 19272 => 24051,
19273 => 26494, 19274 => 32824, 19275 => 24578, 19276 => 39042, 19277 => 36865,
19278 => 23435, 19279 => 35772, 19280 => 35829, 19281 => 25628, 19282 => 33368,
19283 => 25822, 19284 => 22013, 19285 => 33487, 19286 => 37221, 19287 => 20439,
19288 => 32032, 19289 => 36895, 19290 => 31903, 19291 => 20723, 19292 => 22609,
19293 => 28335, 19294 => 23487, 19295 => 35785, 19296 => 32899, 19297 => 37240,
19298 => 33948, 19299 => 31639, 19300 => 34429, 19301 => 38539, 19302 => 38543,
19303 => 32485, 19304 => 39635, 19305 => 30862, 19306 => 23681, 19307 => 31319,
19308 => 36930, 19309 => 38567, 19310 => 31071, 19311 => 23385, 19312 => 25439,
19313 => 31499, 19314 => 34001, 19315 => 26797, 19316 => 21766, 19317 => 32553,
19318 => 29712, 19319 => 32034, 19320 => 38145, 19321 => 25152, 19322 => 22604,
19323 => 20182, 19324 => 23427, 19325 => 22905, 19326 => 22612, 19489 => 29549,
19490 => 25374, 19491 => 36427, 19492 => 36367, 19493 => 32974, 19494 => 33492,
19495 => 25260, 19496 => 21488, 19497 => 27888, 19498 => 37214, 19499 => 22826,
19500 => 24577, 19501 => 27760, 19502 => 22349, 19503 => 25674, 19504 => 36138,
19505 => 30251, 19506 => 28393, 19507 => 22363, 19508 => 27264, 19509 => 30192,
19510 => 28525, 19511 => 35885, 19512 => 35848, 19513 => 22374, 19514 => 27631,
19515 => 34962, 19516 => 30899, 19517 => 25506, 19518 => 21497, 19519 => 28845,
19520 => 27748, 19521 => 22616, 19522 => 25642, 19523 => 22530, 19524 => 26848,
19525 => 33179, 19526 => 21776, 19527 => 31958, 19528 => 20504, 19529 => 36538,
19530 => 28108, 19531 => 36255, 19532 => 28907, 19533 => 25487, 19534 => 28059,
19535 => 28372, 19536 => 32486, 19537 => 33796, 19538 => 26691, 19539 => 36867,
19540 => 28120, 19541 => 38518, 19542 => 35752, 19543 => 22871, 19544 => 29305,
19545 => 34276, 19546 => 33150, 19547 => 30140, 19548 => 35466, 19549 => 26799,
19550 => 21076, 19551 => 36386, 19552 => 38161, 19553 => 25552, 19554 => 39064,
19555 => 36420, 19556 => 21884, 19557 => 20307, 19558 => 26367, 19559 => 22159,
19560 => 24789, 19561 => 28053, 19562 => 21059, 19563 => 23625, 19564 => 22825,
19565 => 28155, 19566 => 22635, 19567 => 30000, 19568 => 29980, 19569 => 24684,
19570 => 33300, 19571 => 33094, 19572 => 25361, 19573 => 26465, 19574 => 36834,
19575 => 30522, 19576 => 36339, 19577 => 36148, 19578 => 38081, 19579 => 24086,
19580 => 21381, 19581 => 21548, 19582 => 28867, 19745 => 27712, 19746 => 24311,
19747 => 20572, 19748 => 20141, 19749 => 24237, 19750 => 25402, 19751 => 33351,
19752 => 36890, 19753 => 26704, 19754 => 37230, 19755 => 30643, 19756 => 21516,
19757 => 38108, 19758 => 24420, 19759 => 31461, 19760 => 26742, 19761 => 25413,
19762 => 31570, 19763 => 32479, 19764 => 30171, 19765 => 20599, 19766 => 25237,
19767 => 22836, 19768 => 36879, 19769 => 20984, 19770 => 31171, 19771 => 31361,
19772 => 22270, 19773 => 24466, 19774 => 36884, 19775 => 28034, 19776 => 23648,
19777 => 22303, 19778 => 21520, 19779 => 20820, 19780 => 28237, 19781 => 22242,
19782 => 25512, 19783 => 39059, 19784 => 33151, 19785 => 34581, 19786 => 35114,
19787 => 36864, 19788 => 21534, 19789 => 23663, 19790 => 33216, 19791 => 25302,
19792 => 25176, 19793 => 33073, 19794 => 40501, 19795 => 38464, 19796 => 39534,
19797 => 39548, 19798 => 26925, 19799 => 22949, 19800 => 25299, 19801 => 21822,
19802 => 25366, 19803 => 21703, 19804 => 34521, 19805 => 27964, 19806 => 23043,
19807 => 29926, 19808 => 34972, 19809 => 27498, 19810 => 22806, 19811 => 35916,
19812 => 24367, 19813 => 28286, 19814 => 29609, 19815 => 39037, 19816 => 20024,
19817 => 28919, 19818 => 23436, 19819 => 30871, 19820 => 25405, 19821 => 26202,
19822 => 30358, 19823 => 24779, 19824 => 23451, 19825 => 23113, 19826 => 19975,
19827 => 33109, 19828 => 27754, 19829 => 29579, 19830 => 20129, 19831 => 26505,
19832 => 32593, 19833 => 24448, 19834 => 26106, 19835 => 26395, 19836 => 24536,
19837 => 22916, 19838 => 23041, 20001 => 24013, 20002 => 24494, 20003 => 21361,
20004 => 38886, 20005 => 36829, 20006 => 26693, 20007 => 22260, 20008 => 21807,
20009 => 24799, 20010 => 20026, 20011 => 28493, 20012 => 32500, 20013 => 33479,
20014 => 33806, 20015 => 22996, 20016 => 20255, 20017 => 20266, 20018 => 23614,
20019 => 32428, 20020 => 26410, 20021 => 34074, 20022 => 21619, 20023 => 30031,
20024 => 32963, 20025 => 21890, 20026 => 39759, 20027 => 20301, 20028 => 28205,
20029 => 35859, 20030 => 23561, 20031 => 24944, 20032 => 21355, 20033 => 30239,
20034 => 28201, 20035 => 34442, 20036 => 25991, 20037 => 38395, 20038 => 32441,
20039 => 21563, 20040 => 31283, 20041 => 32010, 20042 => 38382, 20043 => 21985,
20044 => 32705, 20045 => 29934, 20046 => 25373, 20047 => 34583, 20048 => 28065,
20049 => 31389, 20050 => 25105, 20051 => 26017, 20052 => 21351, 20053 => 25569,
20054 => 27779, 20055 => 24043, 20056 => 21596, 20057 => 38056, 20058 => 20044,
20059 => 27745, 20060 => 35820, 20061 => 23627, 20062 => 26080, 20063 => 33436,
20064 => 26791, 20065 => 21566, 20066 => 21556, 20067 => 27595, 20068 => 27494,
20069 => 20116, 20070 => 25410, 20071 => 21320, 20072 => 33310, 20073 => 20237,
20074 => 20398, 20075 => 22366, 20076 => 25098, 20077 => 38654, 20078 => 26212,
20079 => 29289, 20080 => 21247, 20081 => 21153, 20082 => 24735, 20083 => 35823,
20084 => 26132, 20085 => 29081, 20086 => 26512, 20087 => 35199, 20088 => 30802,
20089 => 30717, 20090 => 26224, 20091 => 22075, 20092 => 21560, 20093 => 38177,
20094 => 29306, 20257 => 31232, 20258 => 24687, 20259 => 24076, 20260 => 24713,
20261 => 33181, 20262 => 22805, 20263 => 24796, 20264 => 29060, 20265 => 28911,
20266 => 28330, 20267 => 27728, 20268 => 29312, 20269 => 27268, 20270 => 34989,
20271 => 24109, 20272 => 20064, 20273 => 23219, 20274 => 21916, 20275 => 38115,
20276 => 27927, 20277 => 31995, 20278 => 38553, 20279 => 25103, 20280 => 32454,
20281 => 30606, 20282 => 34430, 20283 => 21283, 20284 => 38686, 20285 => 36758,
20286 => 26247, 20287 => 23777, 20288 => 20384, 20289 => 29421, 20290 => 19979,
20291 => 21414, 20292 => 22799, 20293 => 21523, 20294 => 25472, 20295 => 38184,
20296 => 20808, 20297 => 20185, 20298 => 40092, 20299 => 32420, 20300 => 21688,
20301 => 36132, 20302 => 34900, 20303 => 33335, 20304 => 38386, 20305 => 28046,
20306 => 24358, 20307 => 23244, 20308 => 26174, 20309 => 38505, 20310 => 29616,
20311 => 29486, 20312 => 21439, 20313 => 33146, 20314 => 39301, 20315 => 32673,
20316 => 23466, 20317 => 38519, 20318 => 38480, 20319 => 32447, 20320 => 30456,
20321 => 21410, 20322 => 38262, 20323 => 39321, 20324 => 31665, 20325 => 35140,
20326 => 28248, 20327 => 20065, 20328 => 32724, 20329 => 31077, 20330 => 35814,
20331 => 24819, 20332 => 21709, 20333 => 20139, 20334 => 39033, 20335 => 24055,
20336 => 27233, 20337 => 20687, 20338 => 21521, 20339 => 35937, 20340 => 33831,
20341 => 30813, 20342 => 38660, 20343 => 21066, 20344 => 21742, 20345 => 22179,
20346 => 38144, 20347 => 28040, 20348 => 23477, 20349 => 28102, 20350 => 26195,
20513 => 23567, 20514 => 23389, 20515 => 26657, 20516 => 32918, 20517 => 21880,
20518 => 31505, 20519 => 25928, 20520 => 26964, 20521 => 20123, 20522 => 27463,
20523 => 34638, 20524 => 38795, 20525 => 21327, 20526 => 25375, 20527 => 25658,
20528 => 37034, 20529 => 26012, 20530 => 32961, 20531 => 35856, 20532 => 20889,
20533 => 26800, 20534 => 21368, 20535 => 34809, 20536 => 25032, 20537 => 27844,
20538 => 27899, 20539 => 35874, 20540 => 23633, 20541 => 34218, 20542 => 33455,
20543 => 38156, 20544 => 27427, 20545 => 36763, 20546 => 26032, 20547 => 24571,
20548 => 24515, 20549 => 20449, 20550 => 34885, 20551 => 26143, 20552 => 33125,
20553 => 29481, 20554 => 24826, 20555 => 20852, 20556 => 21009, 20557 => 22411,
20558 => 24418, 20559 => 37026, 20560 => 34892, 20561 => 37266, 20562 => 24184,
20563 => 26447, 20564 => 24615, 20565 => 22995, 20566 => 20804, 20567 => 20982,
20568 => 33016, 20569 => 21256, 20570 => 27769, 20571 => 38596, 20572 => 29066,
20573 => 20241, 20574 => 20462, 20575 => 32670, 20576 => 26429, 20577 => 21957,
20578 => 38152, 20579 => 31168, 20580 => 34966, 20581 => 32483, 20582 => 22687,
20583 => 25100, 20584 => 38656, 20585 => 34394, 20586 => 22040, 20587 => 39035,
20588 => 24464, 20589 => 35768, 20590 => 33988, 20591 => 37207, 20592 => 21465,
20593 => 26093, 20594 => 24207, 20595 => 30044, 20596 => 24676, 20597 => 32110,
20598 => 23167, 20599 => 32490, 20600 => 32493, 20601 => 36713, 20602 => 21927,
20603 => 23459, 20604 => 24748, 20605 => 26059, 20606 => 29572, 20769 => 36873,
20770 => 30307, 20771 => 30505, 20772 => 32474, 20773 => 38772, 20774 => 34203,
20775 => 23398, 20776 => 31348, 20777 => 38634, 20778 => 34880, 20779 => 21195,
20780 => 29071, 20781 => 24490, 20782 => 26092, 20783 => 35810, 20784 => 23547,
20785 => 39535, 20786 => 24033, 20787 => 27529, 20788 => 27739, 20789 => 35757,
20790 => 35759, 20791 => 36874, 20792 => 36805, 20793 => 21387, 20794 => 25276,
20795 => 40486, 20796 => 40493, 20797 => 21568, 20798 => 20011, 20799 => 33469,
20800 => 29273, 20801 => 34460, 20802 => 23830, 20803 => 34905, 20804 => 28079,
20805 => 38597, 20806 => 21713, 20807 => 20122, 20808 => 35766, 20809 => 28937,
20810 => 21693, 20811 => 38409, 20812 => 28895, 20813 => 28153, 20814 => 30416,
20815 => 20005, 20816 => 30740, 20817 => 34578, 20818 => 23721, 20819 => 24310,
20820 => 35328, 20821 => 39068, 20822 => 38414, 20823 => 28814, 20824 => 27839,
20825 => 22852, 20826 => 25513, 20827 => 30524, 20828 => 34893, 20829 => 28436,
20830 => 33395, 20831 => 22576, 20832 => 29141, 20833 => 21388, 20834 => 30746,
20835 => 38593, 20836 => 21761, 20837 => 24422, 20838 => 28976, 20839 => 23476,
20840 => 35866, 20841 => 39564, 20842 => 27523, 20843 => 22830, 20844 => 40495,
20845 => 31207, 20846 => 26472, 20847 => 25196, 20848 => 20335, 20849 => 30113,
20850 => 32650, 20851 => 27915, 20852 => 38451, 20853 => 27687, 20854 => 20208,
20855 => 30162, 20856 => 20859, 20857 => 26679, 20858 => 28478, 20859 => 36992,
20860 => 33136, 20861 => 22934, 20862 => 29814, 21025 => 25671, 21026 => 23591,
21027 => 36965, 21028 => 31377, 21029 => 35875, 21030 => 23002, 21031 => 21676,
21032 => 33280, 21033 => 33647, 21034 => 35201, 21035 => 32768, 21036 => 26928,
21037 => 22094, 21038 => 32822, 21039 => 29239, 21040 => 37326, 21041 => 20918,
21042 => 20063, 21043 => 39029, 21044 => 25494, 21045 => 19994, 21046 => 21494,
21047 => 26355, 21048 => 33099, 21049 => 22812, 21050 => 28082, 21051 => 19968,
21052 => 22777, 21053 => 21307, 21054 => 25558, 21055 => 38129, 21056 => 20381,
21057 => 20234, 21058 => 34915, 21059 => 39056, 21060 => 22839, 21061 => 36951,
21062 => 31227, 21063 => 20202, 21064 => 33008, 21065 => 30097, 21066 => 27778,
21067 => 23452, 21068 => 23016, 21069 => 24413, 21070 => 26885, 21071 => 34433,
21072 => 20506, 21073 => 24050, 21074 => 20057, 21075 => 30691, 21076 => 20197,
21077 => 33402, 21078 => 25233, 21079 => 26131, 21080 => 37009, 21081 => 23673,
21082 => 20159, 21083 => 24441, 21084 => 33222, 21085 => 36920, 21086 => 32900,
21087 => 30123, 21088 => 20134, 21089 => 35028, 21090 => 24847, 21091 => 27589,
21092 => 24518, 21093 => 20041, 21094 => 30410, 21095 => 28322, 21096 => 35811,
21097 => 35758, 21098 => 35850, 21099 => 35793, 21100 => 24322, 21101 => 32764,
21102 => 32716, 21103 => 32462, 21104 => 33589, 21105 => 33643, 21106 => 22240,
21107 => 27575, 21108 => 38899, 21109 => 38452, 21110 => 23035, 21111 => 21535,
21112 => 38134, 21113 => 28139, 21114 => 23493, 21115 => 39278, 21116 => 23609,
21117 => 24341, 21118 => 38544, 21281 => 21360, 21282 => 33521, 21283 => 27185,
21284 => 23156, 21285 => 40560, 21286 => 24212, 21287 => 32552, 21288 => 33721,
21289 => 33828, 21290 => 33829, 21291 => 33639, 21292 => 34631, 21293 => 36814,
21294 => 36194, 21295 => 30408, 21296 => 24433, 21297 => 39062, 21298 => 30828,
21299 => 26144, 21300 => 21727, 21301 => 25317, 21302 => 20323, 21303 => 33219,
21304 => 30152, 21305 => 24248, 21306 => 38605, 21307 => 36362, 21308 => 34553,
21309 => 21647, 21310 => 27891, 21311 => 28044, 21312 => 27704, 21313 => 24703,
21314 => 21191, 21315 => 29992, 21316 => 24189, 21317 => 20248, 21318 => 24736,
21319 => 24551, 21320 => 23588, 21321 => 30001, 21322 => 37038, 21323 => 38080,
21324 => 29369, 21325 => 27833, 21326 => 28216, 21327 => 37193, 21328 => 26377,
21329 => 21451, 21330 => 21491, 21331 => 20305, 21332 => 37321, 21333 => 35825,
21334 => 21448, 21335 => 24188, 21336 => 36802, 21337 => 28132, 21338 => 20110,
21339 => 30402, 21340 => 27014, 21341 => 34398, 21342 => 24858, 21343 => 33286,
21344 => 20313, 21345 => 20446, 21346 => 36926, 21347 => 40060, 21348 => 24841,
21349 => 28189, 21350 => 28180, 21351 => 38533, 21352 => 20104, 21353 => 23089,
21354 => 38632, 21355 => 19982, 21356 => 23679, 21357 => 31161, 21358 => 23431,
21359 => 35821, 21360 => 32701, 21361 => 29577, 21362 => 22495, 21363 => 33419,
21364 => 37057, 21365 => 21505, 21366 => 36935, 21367 => 21947, 21368 => 23786,
21369 => 24481, 21370 => 24840, 21371 => 27442, 21372 => 29425, 21373 => 32946,
21374 => 35465, 21537 => 28020, 21538 => 23507, 21539 => 35029, 21540 => 39044,
21541 => 35947, 21542 => 39533, 21543 => 40499, 21544 => 28170, 21545 => 20900,
21546 => 20803, 21547 => 22435, 21548 => 34945, 21549 => 21407, 21550 => 25588,
21551 => 36757, 21552 => 22253, 21553 => 21592, 21554 => 22278, 21555 => 29503,
21556 => 28304, 21557 => 32536, 21558 => 36828, 21559 => 33489, 21560 => 24895,
21561 => 24616, 21562 => 38498, 21563 => 26352, 21564 => 32422, 21565 => 36234,
21566 => 36291, 21567 => 38053, 21568 => 23731, 21569 => 31908, 21570 => 26376,
21571 => 24742, 21572 => 38405, 21573 => 32792, 21574 => 20113, 21575 => 37095,
21576 => 21248, 21577 => 38504, 21578 => 20801, 21579 => 36816, 21580 => 34164,
21581 => 37213, 21582 => 26197, 21583 => 38901, 21584 => 23381, 21585 => 21277,
21586 => 30776, 21587 => 26434, 21588 => 26685, 21589 => 21705, 21590 => 28798,
21591 => 23472, 21592 => 36733, 21593 => 20877, 21594 => 22312, 21595 => 21681,
21596 => 25874, 21597 => 26242, 21598 => 36190, 21599 => 36163, 21600 => 33039,
21601 => 33900, 21602 => 36973, 21603 => 31967, 21604 => 20991, 21605 => 34299,
21606 => 26531, 21607 => 26089, 21608 => 28577, 21609 => 34468, 21610 => 36481,
21611 => 22122, 21612 => 36896, 21613 => 30338, 21614 => 28790, 21615 => 29157,
21616 => 36131, 21617 => 25321, 21618 => 21017, 21619 => 27901, 21620 => 36156,
21621 => 24590, 21622 => 22686, 21623 => 24974, 21624 => 26366, 21625 => 36192,
21626 => 25166, 21627 => 21939, 21628 => 28195, 21629 => 26413, 21630 => 36711,
21793 => 38113, 21794 => 38392, 21795 => 30504, 21796 => 26629, 21797 => 27048,
21798 => 21643, 21799 => 20045, 21800 => 28856, 21801 => 35784, 21802 => 25688,
21803 => 25995, 21804 => 23429, 21805 => 31364, 21806 => 20538, 21807 => 23528,
21808 => 30651, 21809 => 27617, 21810 => 35449, 21811 => 31896, 21812 => 27838,
21813 => 30415, 21814 => 26025, 21815 => 36759, 21816 => 23853, 21817 => 23637,
21818 => 34360, 21819 => 26632, 21820 => 21344, 21821 => 25112, 21822 => 31449,
21823 => 28251, 21824 => 32509, 21825 => 27167, 21826 => 31456, 21827 => 24432,
21828 => 28467, 21829 => 24352, 21830 => 25484, 21831 => 28072, 21832 => 26454,
21833 => 19976, 21834 => 24080, 21835 => 36134, 21836 => 20183, 21837 => 32960,
21838 => 30260, 21839 => 38556, 21840 => 25307, 21841 => 26157, 21842 => 25214,
21843 => 27836, 21844 => 36213, 21845 => 29031, 21846 => 32617, 21847 => 20806,
21848 => 32903, 21849 => 21484, 21850 => 36974, 21851 => 25240, 21852 => 21746,
21853 => 34544, 21854 => 36761, 21855 => 32773, 21856 => 38167, 21857 => 34071,
21858 => 36825, 21859 => 27993, 21860 => 29645, 21861 => 26015, 21862 => 30495,
21863 => 29956, 21864 => 30759, 21865 => 33275, 21866 => 36126, 21867 => 38024,
21868 => 20390, 21869 => 26517, 21870 => 30137, 21871 => 35786, 21872 => 38663,
21873 => 25391, 21874 => 38215, 21875 => 38453, 21876 => 33976, 21877 => 25379,
21878 => 30529, 21879 => 24449, 21880 => 29424, 21881 => 20105, 21882 => 24596,
21883 => 25972, 21884 => 25327, 21885 => 27491, 21886 => 25919, 22049 => 24103,
22050 => 30151, 22051 => 37073, 22052 => 35777, 22053 => 33437, 22054 => 26525,
22055 => 25903, 22056 => 21553, 22057 => 34584, 22058 => 30693, 22059 => 32930,
22060 => 33026, 22061 => 27713, 22062 => 20043, 22063 => 32455, 22064 => 32844,
22065 => 30452, 22066 => 26893, 22067 => 27542, 22068 => 25191, 22069 => 20540,
22070 => 20356, 22071 => 22336, 22072 => 25351, 22073 => 27490, 22074 => 36286,
22075 => 21482, 22076 => 26088, 22077 => 32440, 22078 => 24535, 22079 => 25370,
22080 => 25527, 22081 => 33267, 22082 => 33268, 22083 => 32622, 22084 => 24092,
22085 => 23769, 22086 => 21046, 22087 => 26234, 22088 => 31209, 22089 => 31258,
22090 => 36136, 22091 => 28825, 22092 => 30164, 22093 => 28382, 22094 => 27835,
22095 => 31378, 22096 => 20013, 22097 => 30405, 22098 => 24544, 22099 => 38047,
22100 => 34935, 22101 => 32456, 22102 => 31181, 22103 => 32959, 22104 => 37325,
22105 => 20210, 22106 => 20247, 22107 => 33311, 22108 => 21608, 22109 => 24030,
22110 => 27954, 22111 => 35788, 22112 => 31909, 22113 => 36724, 22114 => 32920,
22115 => 24090, 22116 => 21650, 22117 => 30385, 22118 => 23449, 22119 => 26172,
22120 => 39588, 22121 => 29664, 22122 => 26666, 22123 => 34523, 22124 => 26417,
22125 => 29482, 22126 => 35832, 22127 => 35803, 22128 => 36880, 22129 => 31481,
22130 => 28891, 22131 => 29038, 22132 => 25284, 22133 => 30633, 22134 => 22065,
22135 => 20027, 22136 => 33879, 22137 => 26609, 22138 => 21161, 22139 => 34496,
22140 => 36142, 22141 => 38136, 22142 => 31569, 22305 => 20303, 22306 => 27880,
22307 => 31069, 22308 => 39547, 22309 => 25235, 22310 => 29226, 22311 => 25341,
22312 => 19987, 22313 => 30742, 22314 => 36716, 22315 => 25776, 22316 => 36186,
22317 => 31686, 22318 => 26729, 22319 => 24196, 22320 => 35013, 22321 => 22918,
22322 => 25758, 22323 => 22766, 22324 => 29366, 22325 => 26894, 22326 => 38181,
22327 => 36861, 22328 => 36184, 22329 => 22368, 22330 => 32512, 22331 => 35846,
22332 => 20934, 22333 => 25417, 22334 => 25305, 22335 => 21331, 22336 => 26700,
22337 => 29730, 22338 => 33537, 22339 => 37196, 22340 => 21828, 22341 => 30528,
22342 => 28796, 22343 => 27978, 22344 => 20857, 22345 => 21672, 22346 => 36164,
22347 => 23039, 22348 => 28363, 22349 => 28100, 22350 => 23388, 22351 => 32043,
22352 => 20180, 22353 => 31869, 22354 => 28371, 22355 => 23376, 22356 => 33258,
22357 => 28173, 22358 => 23383, 22359 => 39683, 22360 => 26837, 22361 => 36394,
22362 => 23447, 22363 => 32508, 22364 => 24635, 22365 => 32437, 22366 => 37049,
22367 => 36208, 22368 => 22863, 22369 => 25549, 22370 => 31199, 22371 => 36275,
22372 => 21330, 22373 => 26063, 22374 => 31062, 22375 => 35781, 22376 => 38459,
22377 => 32452, 22378 => 38075, 22379 => 32386, 22380 => 22068, 22381 => 37257,
22382 => 26368, 22383 => 32618, 22384 => 23562, 22385 => 36981, 22386 => 26152,
22387 => 24038, 22388 => 20304, 22389 => 26590, 22390 => 20570, 22391 => 20316,
22392 => 22352, 22393 => 24231, 22561 => 20109, 22562 => 19980, 22563 => 20800,
22564 => 19984, 22565 => 24319, 22566 => 21317, 22567 => 19989, 22568 => 20120,
22569 => 19998, 22570 => 39730, 22571 => 23404, 22572 => 22121, 22573 => 20008,
22574 => 31162, 22575 => 20031, 22576 => 21269, 22577 => 20039, 22578 => 22829,
22579 => 29243, 22580 => 21358, 22581 => 27664, 22582 => 22239, 22583 => 32996,
22584 => 39319, 22585 => 27603, 22586 => 30590, 22587 => 40727, 22588 => 20022,
22589 => 20127, 22590 => 40720, 22591 => 20060, 22592 => 20073, 22593 => 20115,
22594 => 33416, 22595 => 23387, 22596 => 21868, 22597 => 22031, 22598 => 20164,
22599 => 21389, 22600 => 21405, 22601 => 21411, 22602 => 21413, 22603 => 21422,
22604 => 38757, 22605 => 36189, 22606 => 21274, 22607 => 21493, 22608 => 21286,
22609 => 21294, 22610 => 21310, 22611 => 36188, 22612 => 21350, 22613 => 21347,
22614 => 20994, 22615 => 21000, 22616 => 21006, 22617 => 21037, 22618 => 21043,
22619 => 21055, 22620 => 21056, 22621 => 21068, 22622 => 21086, 22623 => 21089,
22624 => 21084, 22625 => 33967, 22626 => 21117, 22627 => 21122, 22628 => 21121,
22629 => 21136, 22630 => 21139, 22631 => 20866, 22632 => 32596, 22633 => 20155,
22634 => 20163, 22635 => 20169, 22636 => 20162, 22637 => 20200, 22638 => 20193,
22639 => 20203, 22640 => 20190, 22641 => 20251, 22642 => 20211, 22643 => 20258,
22644 => 20324, 22645 => 20213, 22646 => 20261, 22647 => 20263, 22648 => 20233,
22649 => 20267, 22650 => 20318, 22651 => 20327, 22652 => 25912, 22653 => 20314,
22654 => 20317, 22817 => 20319, 22818 => 20311, 22819 => 20274, 22820 => 20285,
22821 => 20342, 22822 => 20340, 22823 => 20369, 22824 => 20361, 22825 => 20355,
22826 => 20367, 22827 => 20350, 22828 => 20347, 22829 => 20394, 22830 => 20348,
22831 => 20396, 22832 => 20372, 22833 => 20454, 22834 => 20456, 22835 => 20458,
22836 => 20421, 22837 => 20442, 22838 => 20451, 22839 => 20444, 22840 => 20433,
22841 => 20447, 22842 => 20472, 22843 => 20521, 22844 => 20556, 22845 => 20467,
22846 => 20524, 22847 => 20495, 22848 => 20526, 22849 => 20525, 22850 => 20478,
22851 => 20508, 22852 => 20492, 22853 => 20517, 22854 => 20520, 22855 => 20606,
22856 => 20547, 22857 => 20565, 22858 => 20552, 22859 => 20558, 22860 => 20588,
22861 => 20603, 22862 => 20645, 22863 => 20647, 22864 => 20649, 22865 => 20666,
22866 => 20694, 22867 => 20742, 22868 => 20717, 22869 => 20716, 22870 => 20710,
22871 => 20718, 22872 => 20743, 22873 => 20747, 22874 => 20189, 22875 => 27709,
22876 => 20312, 22877 => 20325, 22878 => 20430, 22879 => 40864, 22880 => 27718,
22881 => 31860, 22882 => 20846, 22883 => 24061, 22884 => 40649, 22885 => 39320,
22886 => 20865, 22887 => 22804, 22888 => 21241, 22889 => 21261, 22890 => 35335,
22891 => 21264, 22892 => 20971, 22893 => 22809, 22894 => 20821, 22895 => 20128,
22896 => 20822, 22897 => 20147, 22898 => 34926, 22899 => 34980, 22900 => 20149,
22901 => 33044, 22902 => 35026, 22903 => 31104, 22904 => 23348, 22905 => 34819,
22906 => 32696, 22907 => 20907, 22908 => 20913, 22909 => 20925, 22910 => 20924,
23073 => 20935, 23074 => 20886, 23075 => 20898, 23076 => 20901, 23077 => 35744,
23078 => 35750, 23079 => 35751, 23080 => 35754, 23081 => 35764, 23082 => 35765,
23083 => 35767, 23084 => 35778, 23085 => 35779, 23086 => 35787, 23087 => 35791,
23088 => 35790, 23089 => 35794, 23090 => 35795, 23091 => 35796, 23092 => 35798,
23093 => 35800, 23094 => 35801, 23095 => 35804, 23096 => 35807, 23097 => 35808,
23098 => 35812, 23099 => 35816, 23100 => 35817, 23101 => 35822, 23102 => 35824,
23103 => 35827, 23104 => 35830, 23105 => 35833, 23106 => 35836, 23107 => 35839,
23108 => 35840, 23109 => 35842, 23110 => 35844, 23111 => 35847, 23112 => 35852,
23113 => 35855, 23114 => 35857, 23115 => 35858, 23116 => 35860, 23117 => 35861,
23118 => 35862, 23119 => 35865, 23120 => 35867, 23121 => 35864, 23122 => 35869,
23123 => 35871, 23124 => 35872, 23125 => 35873, 23126 => 35877, 23127 => 35879,
23128 => 35882, 23129 => 35883, 23130 => 35886, 23131 => 35887, 23132 => 35890,
23133 => 35891, 23134 => 35893, 23135 => 35894, 23136 => 21353, 23137 => 21370,
23138 => 38429, 23139 => 38434, 23140 => 38433, 23141 => 38449, 23142 => 38442,
23143 => 38461, 23144 => 38460, 23145 => 38466, 23146 => 38473, 23147 => 38484,
23148 => 38495, 23149 => 38503, 23150 => 38508, 23151 => 38514, 23152 => 38516,
23153 => 38536, 23154 => 38541, 23155 => 38551, 23156 => 38576, 23157 => 37015,
23158 => 37019, 23159 => 37021, 23160 => 37017, 23161 => 37036, 23162 => 37025,
23163 => 37044, 23164 => 37043, 23165 => 37046, 23166 => 37050, 23329 => 37048,
23330 => 37040, 23331 => 37071, 23332 => 37061, 23333 => 37054, 23334 => 37072,
23335 => 37060, 23336 => 37063, 23337 => 37075, 23338 => 37094, 23339 => 37090,
23340 => 37084, 23341 => 37079, 23342 => 37083, 23343 => 37099, 23344 => 37103,
23345 => 37118, 23346 => 37124, 23347 => 37154, 23348 => 37150, 23349 => 37155,
23350 => 37169, 23351 => 37167, 23352 => 37177, 23353 => 37187, 23354 => 37190,
23355 => 21005, 23356 => 22850, 23357 => 21154, 23358 => 21164, 23359 => 21165,
23360 => 21182, 23361 => 21759, 23362 => 21200, 23363 => 21206, 23364 => 21232,
23365 => 21471, 23366 => 29166, 23367 => 30669, 23368 => 24308, 23369 => 20981,
23370 => 20988, 23371 => 39727, 23372 => 21430, 23373 => 24321, 23374 => 30042,
23375 => 24047, 23376 => 22348, 23377 => 22441, 23378 => 22433, 23379 => 22654,
23380 => 22716, 23381 => 22725, 23382 => 22737, 23383 => 22313, 23384 => 22316,
23385 => 22314, 23386 => 22323, 23387 => 22329, 23388 => 22318, 23389 => 22319,
23390 => 22364, 23391 => 22331, 23392 => 22338, 23393 => 22377, 23394 => 22405,
23395 => 22379, 23396 => 22406, 23397 => 22396, 23398 => 22395, 23399 => 22376,
23400 => 22381, 23401 => 22390, 23402 => 22387, 23403 => 22445, 23404 => 22436,
23405 => 22412, 23406 => 22450, 23407 => 22479, 23408 => 22439, 23409 => 22452,
23410 => 22419, 23411 => 22432, 23412 => 22485, 23413 => 22488, 23414 => 22490,
23415 => 22489, 23416 => 22482, 23417 => 22456, 23418 => 22516, 23419 => 22511,
23420 => 22520, 23421 => 22500, 23422 => 22493, 23585 => 22539, 23586 => 22541,
23587 => 22525, 23588 => 22509, 23589 => 22528, 23590 => 22558, 23591 => 22553,
23592 => 22596, 23593 => 22560, 23594 => 22629, 23595 => 22636, 23596 => 22657,
23597 => 22665, 23598 => 22682, 23599 => 22656, 23600 => 39336, 23601 => 40729,
23602 => 25087, 23603 => 33401, 23604 => 33405, 23605 => 33407, 23606 => 33423,
23607 => 33418, 23608 => 33448, 23609 => 33412, 23610 => 33422, 23611 => 33425,
23612 => 33431, 23613 => 33433, 23614 => 33451, 23615 => 33464, 23616 => 33470,
23617 => 33456, 23618 => 33480, 23619 => 33482, 23620 => 33507, 23621 => 33432,
23622 => 33463, 23623 => 33454, 23624 => 33483, 23625 => 33484, 23626 => 33473,
23627 => 33449, 23628 => 33460, 23629 => 33441, 23630 => 33450, 23631 => 33439,
23632 => 33476, 23633 => 33486, 23634 => 33444, 23635 => 33505, 23636 => 33545,
23637 => 33527, 23638 => 33508, 23639 => 33551, 23640 => 33543, 23641 => 33500,
23642 => 33524, 23643 => 33490, 23644 => 33496, 23645 => 33548, 23646 => 33531,
23647 => 33491, 23648 => 33553, 23649 => 33562, 23650 => 33542, 23651 => 33556,
23652 => 33557, 23653 => 33504, 23654 => 33493, 23655 => 33564, 23656 => 33617,
23657 => 33627, 23658 => 33628, 23659 => 33544, 23660 => 33682, 23661 => 33596,
23662 => 33588, 23663 => 33585, 23664 => 33691, 23665 => 33630, 23666 => 33583,
23667 => 33615, 23668 => 33607, 23669 => 33603, 23670 => 33631, 23671 => 33600,
23672 => 33559, 23673 => 33632, 23674 => 33581, 23675 => 33594, 23676 => 33587,
23677 => 33638, 23678 => 33637, 23841 => 33640, 23842 => 33563, 23843 => 33641,
23844 => 33644, 23845 => 33642, 23846 => 33645, 23847 => 33646, 23848 => 33712,
23849 => 33656, 23850 => 33715, 23851 => 33716, 23852 => 33696, 23853 => 33706,
23854 => 33683, 23855 => 33692, 23856 => 33669, 23857 => 33660, 23858 => 33718,
23859 => 33705, 23860 => 33661, 23861 => 33720, 23862 => 33659, 23863 => 33688,
23864 => 33694, 23865 => 33704, 23866 => 33722, 23867 => 33724, 23868 => 33729,
23869 => 33793, 23870 => 33765, 23871 => 33752, 23872 => 22535, 23873 => 33816,
23874 => 33803, 23875 => 33757, 23876 => 33789, 23877 => 33750, 23878 => 33820,
23879 => 33848, 23880 => 33809, 23881 => 33798, 23882 => 33748, 23883 => 33759,
23884 => 33807, 23885 => 33795, 23886 => 33784, 23887 => 33785, 23888 => 33770,
23889 => 33733, 23890 => 33728, 23891 => 33830, 23892 => 33776, 23893 => 33761,
23894 => 33884, 23895 => 33873, 23896 => 33882, 23897 => 33881, 23898 => 33907,
23899 => 33927, 23900 => 33928, 23901 => 33914, 23902 => 33929, 23903 => 33912,
23904 => 33852, 23905 => 33862, 23906 => 33897, 23907 => 33910, 23908 => 33932,
23909 => 33934, 23910 => 33841, 23911 => 33901, 23912 => 33985, 23913 => 33997,
23914 => 34000, 23915 => 34022, 23916 => 33981, 23917 => 34003, 23918 => 33994,
23919 => 33983, 23920 => 33978, 23921 => 34016, 23922 => 33953, 23923 => 33977,
23924 => 33972, 23925 => 33943, 23926 => 34021, 23927 => 34019, 23928 => 34060,
23929 => 29965, 23930 => 34104, 23931 => 34032, 23932 => 34105, 23933 => 34079,
23934 => 34106, 24097 => 34134, 24098 => 34107, 24099 => 34047, 24100 => 34044,
24101 => 34137, 24102 => 34120, 24103 => 34152, 24104 => 34148, 24105 => 34142,
24106 => 34170, 24107 => 30626, 24108 => 34115, 24109 => 34162, 24110 => 34171,
24111 => 34212, 24112 => 34216, 24113 => 34183, 24114 => 34191, 24115 => 34169,
24116 => 34222, 24117 => 34204, 24118 => 34181, 24119 => 34233, 24120 => 34231,
24121 => 34224, 24122 => 34259, 24123 => 34241, 24124 => 34268, 24125 => 34303,
24126 => 34343, 24127 => 34309, 24128 => 34345, 24129 => 34326, 24130 => 34364,
24131 => 24318, 24132 => 24328, 24133 => 22844, 24134 => 22849, 24135 => 32823,
24136 => 22869, 24137 => 22874, 24138 => 22872, 24139 => 21263, 24140 => 23586,
24141 => 23589, 24142 => 23596, 24143 => 23604, 24144 => 25164, 24145 => 25194,
24146 => 25247, 24147 => 25275, 24148 => 25290, 24149 => 25306, 24150 => 25303,
24151 => 25326, 24152 => 25378, 24153 => 25334, 24154 => 25401, 24155 => 25419,
24156 => 25411, 24157 => 25517, 24158 => 25590, 24159 => 25457, 24160 => 25466,
24161 => 25486, 24162 => 25524, 24163 => 25453, 24164 => 25516, 24165 => 25482,
24166 => 25449, 24167 => 25518, 24168 => 25532, 24169 => 25586, 24170 => 25592,
24171 => 25568, 24172 => 25599, 24173 => 25540, 24174 => 25566, 24175 => 25550,
24176 => 25682, 24177 => 25542, 24178 => 25534, 24179 => 25669, 24180 => 25665,
24181 => 25611, 24182 => 25627, 24183 => 25632, 24184 => 25612, 24185 => 25638,
24186 => 25633, 24187 => 25694, 24188 => 25732, 24189 => 25709, 24190 => 25750,
24353 => 25722, 24354 => 25783, 24355 => 25784, 24356 => 25753, 24357 => 25786,
24358 => 25792, 24359 => 25808, 24360 => 25815, 24361 => 25828, 24362 => 25826,
24363 => 25865, 24364 => 25893, 24365 => 25902, 24366 => 24331, 24367 => 24530,
24368 => 29977, 24369 => 24337, 24370 => 21343, 24371 => 21489, 24372 => 21501,
24373 => 21481, 24374 => 21480, 24375 => 21499, 24376 => 21522, 24377 => 21526,
24378 => 21510, 24379 => 21579, 24380 => 21586, 24381 => 21587, 24382 => 21588,
24383 => 21590, 24384 => 21571, 24385 => 21537, 24386 => 21591, 24387 => 21593,
24388 => 21539, 24389 => 21554, 24390 => 21634, 24391 => 21652, 24392 => 21623,
24393 => 21617, 24394 => 21604, 24395 => 21658, 24396 => 21659, 24397 => 21636,
24398 => 21622, 24399 => 21606, 24400 => 21661, 24401 => 21712, 24402 => 21677,
24403 => 21698, 24404 => 21684, 24405 => 21714, 24406 => 21671, 24407 => 21670,
24408 => 21715, 24409 => 21716, 24410 => 21618, 24411 => 21667, 24412 => 21717,
24413 => 21691, 24414 => 21695, 24415 => 21708, 24416 => 21721, 24417 => 21722,
24418 => 21724, 24419 => 21673, 24420 => 21674, 24421 => 21668, 24422 => 21725,
24423 => 21711, 24424 => 21726, 24425 => 21787, 24426 => 21735, 24427 => 21792,
24428 => 21757, 24429 => 21780, 24430 => 21747, 24431 => 21794, 24432 => 21795,
24433 => 21775, 24434 => 21777, 24435 => 21799, 24436 => 21802, 24437 => 21863,
24438 => 21903, 24439 => 21941, 24440 => 21833, 24441 => 21869, 24442 => 21825,
24443 => 21845, 24444 => 21823, 24445 => 21840, 24446 => 21820, 24609 => 21815,
24610 => 21846, 24611 => 21877, 24612 => 21878, 24613 => 21879, 24614 => 21811,
24615 => 21808, 24616 => 21852, 24617 => 21899, 24618 => 21970, 24619 => 21891,
24620 => 21937, 24621 => 21945, 24622 => 21896, 24623 => 21889, 24624 => 21919,
24625 => 21886, 24626 => 21974, 24627 => 21905, 24628 => 21883, 24629 => 21983,
24630 => 21949, 24631 => 21950, 24632 => 21908, 24633 => 21913, 24634 => 21994,
24635 => 22007, 24636 => 21961, 24637 => 22047, 24638 => 21969, 24639 => 21995,
24640 => 21996, 24641 => 21972, 24642 => 21990, 24643 => 21981, 24644 => 21956,
24645 => 21999, 24646 => 21989, 24647 => 22002, 24648 => 22003, 24649 => 21964,
24650 => 21965, 24651 => 21992, 24652 => 22005, 24653 => 21988, 24654 => 36756,
24655 => 22046, 24656 => 22024, 24657 => 22028, 24658 => 22017, 24659 => 22052,
24660 => 22051, 24661 => 22014, 24662 => 22016, 24663 => 22055, 24664 => 22061,
24665 => 22104, 24666 => 22073, 24667 => 22103, 24668 => 22060, 24669 => 22093,
24670 => 22114, 24671 => 22105, 24672 => 22108, 24673 => 22092, 24674 => 22100,
24675 => 22150, 24676 => 22116, 24677 => 22129, 24678 => 22123, 24679 => 22139,
24680 => 22140, 24681 => 22149, 24682 => 22163, 24683 => 22191, 24684 => 22228,
24685 => 22231, 24686 => 22237, 24687 => 22241, 24688 => 22261, 24689 => 22251,
24690 => 22265, 24691 => 22271, 24692 => 22276, 24693 => 22282, 24694 => 22281,
24695 => 22300, 24696 => 24079, 24697 => 24089, 24698 => 24084, 24699 => 24081,
24700 => 24113, 24701 => 24123, 24702 => 24124, 24865 => 24119, 24866 => 24132,
24867 => 24148, 24868 => 24155, 24869 => 24158, 24870 => 24161, 24871 => 23692,
24872 => 23674, 24873 => 23693, 24874 => 23696, 24875 => 23702, 24876 => 23688,
24877 => 23704, 24878 => 23705, 24879 => 23697, 24880 => 23706, 24881 => 23708,
24882 => 23733, 24883 => 23714, 24884 => 23741, 24885 => 23724, 24886 => 23723,
24887 => 23729, 24888 => 23715, 24889 => 23745, 24890 => 23735, 24891 => 23748,
24892 => 23762, 24893 => 23780, 24894 => 23755, 24895 => 23781, 24896 => 23810,
24897 => 23811, 24898 => 23847, 24899 => 23846, 24900 => 23854, 24901 => 23844,
24902 => 23838, 24903 => 23814, 24904 => 23835, 24905 => 23896, 24906 => 23870,
24907 => 23860, 24908 => 23869, 24909 => 23916, 24910 => 23899, 24911 => 23919,
24912 => 23901, 24913 => 23915, 24914 => 23883, 24915 => 23882, 24916 => 23913,
24917 => 23924, 24918 => 23938, 24919 => 23961, 24920 => 23965, 24921 => 35955,
24922 => 23991, 24923 => 24005, 24924 => 24435, 24925 => 24439, 24926 => 24450,
24927 => 24455, 24928 => 24457, 24929 => 24460, 24930 => 24469, 24931 => 24473,
24932 => 24476, 24933 => 24488, 24934 => 24493, 24935 => 24501, 24936 => 24508,
24937 => 34914, 24938 => 24417, 24939 => 29357, 24940 => 29360, 24941 => 29364,
24942 => 29367, 24943 => 29368, 24944 => 29379, 24945 => 29377, 24946 => 29390,
24947 => 29389, 24948 => 29394, 24949 => 29416, 24950 => 29423, 24951 => 29417,
24952 => 29426, 24953 => 29428, 24954 => 29431, 24955 => 29441, 24956 => 29427,
24957 => 29443, 24958 => 29434, 25121 => 29435, 25122 => 29463, 25123 => 29459,
25124 => 29473, 25125 => 29450, 25126 => 29470, 25127 => 29469, 25128 => 29461,
25129 => 29474, 25130 => 29497, 25131 => 29477, 25132 => 29484, 25133 => 29496,
25134 => 29489, 25135 => 29520, 25136 => 29517, 25137 => 29527, 25138 => 29536,
25139 => 29548, 25140 => 29551, 25141 => 29566, 25142 => 33307, 25143 => 22821,
25144 => 39143, 25145 => 22820, 25146 => 22786, 25147 => 39267, 25148 => 39271,
25149 => 39272, 25150 => 39273, 25151 => 39274, 25152 => 39275, 25153 => 39276,
25154 => 39284, 25155 => 39287, 25156 => 39293, 25157 => 39296, 25158 => 39300,
25159 => 39303, 25160 => 39306, 25161 => 39309, 25162 => 39312, 25163 => 39313,
25164 => 39315, 25165 => 39316, 25166 => 39317, 25167 => 24192, 25168 => 24209,
25169 => 24203, 25170 => 24214, 25171 => 24229, 25172 => 24224, 25173 => 24249,
25174 => 24245, 25175 => 24254, 25176 => 24243, 25177 => 36179, 25178 => 24274,
25179 => 24273, 25180 => 24283, 25181 => 24296, 25182 => 24298, 25183 => 33210,
25184 => 24516, 25185 => 24521, 25186 => 24534, 25187 => 24527, 25188 => 24579,
25189 => 24558, 25190 => 24580, 25191 => 24545, 25192 => 24548, 25193 => 24574,
25194 => 24581, 25195 => 24582, 25196 => 24554, 25197 => 24557, 25198 => 24568,
25199 => 24601, 25200 => 24629, 25201 => 24614, 25202 => 24603, 25203 => 24591,
25204 => 24589, 25205 => 24617, 25206 => 24619, 25207 => 24586, 25208 => 24639,
25209 => 24609, 25210 => 24696, 25211 => 24697, 25212 => 24699, 25213 => 24698,
25214 => 24642, 25377 => 24682, 25378 => 24701, 25379 => 24726, 25380 => 24730,
25381 => 24749, 25382 => 24733, 25383 => 24707, 25384 => 24722, 25385 => 24716,
25386 => 24731, 25387 => 24812, 25388 => 24763, 25389 => 24753, 25390 => 24797,
25391 => 24792, 25392 => 24774, 25393 => 24794, 25394 => 24756, 25395 => 24864,
25396 => 24870, 25397 => 24853, 25398 => 24867, 25399 => 24820, 25400 => 24832,
25401 => 24846, 25402 => 24875, 25403 => 24906, 25404 => 24949, 25405 => 25004,
25406 => 24980, 25407 => 24999, 25408 => 25015, 25409 => 25044, 25410 => 25077,
25411 => 24541, 25412 => 38579, 25413 => 38377, 25414 => 38379, 25415 => 38385,
25416 => 38387, 25417 => 38389, 25418 => 38390, 25419 => 38396, 25420 => 38398,
25421 => 38403, 25422 => 38404, 25423 => 38406, 25424 => 38408, 25425 => 38410,
25426 => 38411, 25427 => 38412, 25428 => 38413, 25429 => 38415, 25430 => 38418,
25431 => 38421, 25432 => 38422, 25433 => 38423, 25434 => 38425, 25435 => 38426,
25436 => 20012, 25437 => 29247, 25438 => 25109, 25439 => 27701, 25440 => 27732,
25441 => 27740, 25442 => 27722, 25443 => 27811, 25444 => 27781, 25445 => 27792,
25446 => 27796, 25447 => 27788, 25448 => 27752, 25449 => 27753, 25450 => 27764,
25451 => 27766, 25452 => 27782, 25453 => 27817, 25454 => 27856, 25455 => 27860,
25456 => 27821, 25457 => 27895, 25458 => 27896, 25459 => 27889, 25460 => 27863,
25461 => 27826, 25462 => 27872, 25463 => 27862, 25464 => 27898, 25465 => 27883,
25466 => 27886, 25467 => 27825, 25468 => 27859, 25469 => 27887, 25470 => 27902,
25633 => 27961, 25634 => 27943, 25635 => 27916, 25636 => 27971, 25637 => 27976,
25638 => 27911, 25639 => 27908, 25640 => 27929, 25641 => 27918, 25642 => 27947,
25643 => 27981, 25644 => 27950, 25645 => 27957, 25646 => 27930, 25647 => 27983,
25648 => 27986, 25649 => 27988, 25650 => 27955, 25651 => 28049, 25652 => 28015,
25653 => 28062, 25654 => 28064, 25655 => 27998, 25656 => 28051, 25657 => 28052,
25658 => 27996, 25659 => 28000, 25660 => 28028, 25661 => 28003, 25662 => 28186,
25663 => 28103, 25664 => 28101, 25665 => 28126, 25666 => 28174, 25667 => 28095,
25668 => 28128, 25669 => 28177, 25670 => 28134, 25671 => 28125, 25672 => 28121,
25673 => 28182, 25674 => 28075, 25675 => 28172, 25676 => 28078, 25677 => 28203,
25678 => 28270, 25679 => 28238, 25680 => 28267, 25681 => 28338, 25682 => 28255,
25683 => 28294, 25684 => 28243, 25685 => 28244, 25686 => 28210, 25687 => 28197,
25688 => 28228, 25689 => 28383, 25690 => 28337, 25691 => 28312, 25692 => 28384,
25693 => 28461, 25694 => 28386, 25695 => 28325, 25696 => 28327, 25697 => 28349,
25698 => 28347, 25699 => 28343, 25700 => 28375, 25701 => 28340, 25702 => 28367,
25703 => 28303, 25704 => 28354, 25705 => 28319, 25706 => 28514, 25707 => 28486,
25708 => 28487, 25709 => 28452, 25710 => 28437, 25711 => 28409, 25712 => 28463,
25713 => 28470, 25714 => 28491, 25715 => 28532, 25716 => 28458, 25717 => 28425,
25718 => 28457, 25719 => 28553, 25720 => 28557, 25721 => 28556, 25722 => 28536,
25723 => 28530, 25724 => 28540, 25725 => 28538, 25726 => 28625, 25889 => 28617,
25890 => 28583, 25891 => 28601, 25892 => 28598, 25893 => 28610, 25894 => 28641,
25895 => 28654, 25896 => 28638, 25897 => 28640, 25898 => 28655, 25899 => 28698,
25900 => 28707, 25901 => 28699, 25902 => 28729, 25903 => 28725, 25904 => 28751,
25905 => 28766, 25906 => 23424, 25907 => 23428, 25908 => 23445, 25909 => 23443,
25910 => 23461, 25911 => 23480, 25912 => 29999, 25913 => 39582, 25914 => 25652,
25915 => 23524, 25916 => 23534, 25917 => 35120, 25918 => 23536, 25919 => 36423,
25920 => 35591, 25921 => 36790, 25922 => 36819, 25923 => 36821, 25924 => 36837,
25925 => 36846, 25926 => 36836, 25927 => 36841, 25928 => 36838, 25929 => 36851,
25930 => 36840, 25931 => 36869, 25932 => 36868, 25933 => 36875, 25934 => 36902,
25935 => 36881, 25936 => 36877, 25937 => 36886, 25938 => 36897, 25939 => 36917,
25940 => 36918, 25941 => 36909, 25942 => 36911, 25943 => 36932, 25944 => 36945,
25945 => 36946, 25946 => 36944, 25947 => 36968, 25948 => 36952, 25949 => 36962,
25950 => 36955, 25951 => 26297, 25952 => 36980, 25953 => 36989, 25954 => 36994,
25955 => 37000, 25956 => 36995, 25957 => 37003, 25958 => 24400, 25959 => 24407,
25960 => 24406, 25961 => 24408, 25962 => 23611, 25963 => 21675, 25964 => 23632,
25965 => 23641, 25966 => 23409, 25967 => 23651, 25968 => 23654, 25969 => 32700,
25970 => 24362, 25971 => 24361, 25972 => 24365, 25973 => 33396, 25974 => 24380,
25975 => 39739, 25976 => 23662, 25977 => 22913, 25978 => 22915, 25979 => 22925,
25980 => 22953, 25981 => 22954, 25982 => 22947, 26145 => 22935, 26146 => 22986,
26147 => 22955, 26148 => 22942, 26149 => 22948, 26150 => 22994, 26151 => 22962,
26152 => 22959, 26153 => 22999, 26154 => 22974, 26155 => 23045, 26156 => 23046,
26157 => 23005, 26158 => 23048, 26159 => 23011, 26160 => 23000, 26161 => 23033,
26162 => 23052, 26163 => 23049, 26164 => 23090, 26165 => 23092, 26166 => 23057,
26167 => 23075, 26168 => 23059, 26169 => 23104, 26170 => 23143, 26171 => 23114,
26172 => 23125, 26173 => 23100, 26174 => 23138, 26175 => 23157, 26176 => 33004,
26177 => 23210, 26178 => 23195, 26179 => 23159, 26180 => 23162, 26181 => 23230,
26182 => 23275, 26183 => 23218, 26184 => 23250, 26185 => 23252, 26186 => 23224,
26187 => 23264, 26188 => 23267, 26189 => 23281, 26190 => 23254, 26191 => 23270,
26192 => 23256, 26193 => 23260, 26194 => 23305, 26195 => 23319, 26196 => 23318,
26197 => 23346, 26198 => 23351, 26199 => 23360, 26200 => 23573, 26201 => 23580,
26202 => 23386, 26203 => 23397, 26204 => 23411, 26205 => 23377, 26206 => 23379,
26207 => 23394, 26208 => 39541, 26209 => 39543, 26210 => 39544, 26211 => 39546,
26212 => 39551, 26213 => 39549, 26214 => 39552, 26215 => 39553, 26216 => 39557,
26217 => 39560, 26218 => 39562, 26219 => 39568, 26220 => 39570, 26221 => 39571,
26222 => 39574, 26223 => 39576, 26224 => 39579, 26225 => 39580, 26226 => 39581,
26227 => 39583, 26228 => 39584, 26229 => 39586, 26230 => 39587, 26231 => 39589,
26232 => 39591, 26233 => 32415, 26234 => 32417, 26235 => 32419, 26236 => 32421,
26237 => 32424, 26238 => 32425, 26401 => 32429, 26402 => 32432, 26403 => 32446,
26404 => 32448, 26405 => 32449, 26406 => 32450, 26407 => 32457, 26408 => 32459,
26409 => 32460, 26410 => 32464, 26411 => 32468, 26412 => 32471, 26413 => 32475,
26414 => 32480, 26415 => 32481, 26416 => 32488, 26417 => 32491, 26418 => 32494,
26419 => 32495, 26420 => 32497, 26421 => 32498, 26422 => 32525, 26423 => 32502,
26424 => 32506, 26425 => 32507, 26426 => 32510, 26427 => 32513, 26428 => 32514,
26429 => 32515, 26430 => 32519, 26431 => 32520, 26432 => 32523, 26433 => 32524,
26434 => 32527, 26435 => 32529, 26436 => 32530, 26437 => 32535, 26438 => 32537,
26439 => 32540, 26440 => 32539, 26441 => 32543, 26442 => 32545, 26443 => 32546,
26444 => 32547, 26445 => 32548, 26446 => 32549, 26447 => 32550, 26448 => 32551,
26449 => 32554, 26450 => 32555, 26451 => 32556, 26452 => 32557, 26453 => 32559,
26454 => 32560, 26455 => 32561, 26456 => 32562, 26457 => 32563, 26458 => 32565,
26459 => 24186, 26460 => 30079, 26461 => 24027, 26462 => 30014, 26463 => 37013,
26464 => 29582, 26465 => 29585, 26466 => 29614, 26467 => 29602, 26468 => 29599,
26469 => 29647, 26470 => 29634, 26471 => 29649, 26472 => 29623, 26473 => 29619,
26474 => 29632, 26475 => 29641, 26476 => 29640, 26477 => 29669, 26478 => 29657,
26479 => 39036, 26480 => 29706, 26481 => 29673, 26482 => 29671, 26483 => 29662,
26484 => 29626, 26485 => 29682, 26486 => 29711, 26487 => 29738, 26488 => 29787,
26489 => 29734, 26490 => 29733, 26491 => 29736, 26492 => 29744, 26493 => 29742,
26494 => 29740, 26657 => 29723, 26658 => 29722, 26659 => 29761, 26660 => 29788,
26661 => 29783, 26662 => 29781, 26663 => 29785, 26664 => 29815, 26665 => 29805,
26666 => 29822, 26667 => 29852, 26668 => 29838, 26669 => 29824, 26670 => 29825,
26671 => 29831, 26672 => 29835, 26673 => 29854, 26674 => 29864, 26675 => 29865,
26676 => 29840, 26677 => 29863, 26678 => 29906, 26679 => 29882, 26680 => 38890,
26681 => 38891, 26682 => 38892, 26683 => 26444, 26684 => 26451, 26685 => 26462,
26686 => 26440, 26687 => 26473, 26688 => 26533, 26689 => 26503, 26690 => 26474,
26691 => 26483, 26692 => 26520, 26693 => 26535, 26694 => 26485, 26695 => 26536,
26696 => 26526, 26697 => 26541, 26698 => 26507, 26699 => 26487, 26700 => 26492,
26701 => 26608, 26702 => 26633, 26703 => 26584, 26704 => 26634, 26705 => 26601,
26706 => 26544, 26707 => 26636, 26708 => 26585, 26709 => 26549, 26710 => 26586,
26711 => 26547, 26712 => 26589, 26713 => 26624, 26714 => 26563, 26715 => 26552,
26716 => 26594, 26717 => 26638, 26718 => 26561, 26719 => 26621, 26720 => 26674,
26721 => 26675, 26722 => 26720, 26723 => 26721, 26724 => 26702, 26725 => 26722,
26726 => 26692, 26727 => 26724, 26728 => 26755, 26729 => 26653, 26730 => 26709,
26731 => 26726, 26732 => 26689, 26733 => 26727, 26734 => 26688, 26735 => 26686,
26736 => 26698, 26737 => 26697, 26738 => 26665, 26739 => 26805, 26740 => 26767,
26741 => 26740, 26742 => 26743, 26743 => 26771, 26744 => 26731, 26745 => 26818,
26746 => 26990, 26747 => 26876, 26748 => 26911, 26749 => 26912, 26750 => 26873,
26913 => 26916, 26914 => 26864, 26915 => 26891, 26916 => 26881, 26917 => 26967,
26918 => 26851, 26919 => 26896, 26920 => 26993, 26921 => 26937, 26922 => 26976,
26923 => 26946, 26924 => 26973, 26925 => 27012, 26926 => 26987, 26927 => 27008,
26928 => 27032, 26929 => 27000, 26930 => 26932, 26931 => 27084, 26932 => 27015,
26933 => 27016, 26934 => 27086, 26935 => 27017, 26936 => 26982, 26937 => 26979,
26938 => 27001, 26939 => 27035, 26940 => 27047, 26941 => 27067, 26942 => 27051,
26943 => 27053, 26944 => 27092, 26945 => 27057, 26946 => 27073, 26947 => 27082,
26948 => 27103, 26949 => 27029, 26950 => 27104, 26951 => 27021, 26952 => 27135,
26953 => 27183, 26954 => 27117, 26955 => 27159, 26956 => 27160, 26957 => 27237,
26958 => 27122, 26959 => 27204, 26960 => 27198, 26961 => 27296, 26962 => 27216,
26963 => 27227, 26964 => 27189, 26965 => 27278, 26966 => 27257, 26967 => 27197,
26968 => 27176, 26969 => 27224, 26970 => 27260, 26971 => 27281, 26972 => 27280,
26973 => 27305, 26974 => 27287, 26975 => 27307, 26976 => 29495, 26977 => 29522,
26978 => 27521, 26979 => 27522, 26980 => 27527, 26981 => 27524, 26982 => 27538,
26983 => 27539, 26984 => 27533, 26985 => 27546, 26986 => 27547, 26987 => 27553,
26988 => 27562, 26989 => 36715, 26990 => 36717, 26991 => 36721, 26992 => 36722,
26993 => 36723, 26994 => 36725, 26995 => 36726, 26996 => 36728, 26997 => 36727,
26998 => 36729, 26999 => 36730, 27000 => 36732, 27001 => 36734, 27002 => 36737,
27003 => 36738, 27004 => 36740, 27005 => 36743, 27006 => 36747, 27169 => 36749,
27170 => 36750, 27171 => 36751, 27172 => 36760, 27173 => 36762, 27174 => 36558,
27175 => 25099, 27176 => 25111, 27177 => 25115, 27178 => 25119, 27179 => 25122,
27180 => 25121, 27181 => 25125, 27182 => 25124, 27183 => 25132, 27184 => 33255,
27185 => 29935, 27186 => 29940, 27187 => 29951, 27188 => 29967, 27189 => 29969,
27190 => 29971, 27191 => 25908, 27192 => 26094, 27193 => 26095, 27194 => 26096,
27195 => 26122, 27196 => 26137, 27197 => 26482, 27198 => 26115, 27199 => 26133,
27200 => 26112, 27201 => 28805, 27202 => 26359, 27203 => 26141, 27204 => 26164,
27205 => 26161, 27206 => 26166, 27207 => 26165, 27208 => 32774, 27209 => 26207,
27210 => 26196, 27211 => 26177, 27212 => 26191, 27213 => 26198, 27214 => 26209,
27215 => 26199, 27216 => 26231, 27217 => 26244, 27218 => 26252, 27219 => 26279,
27220 => 26269, 27221 => 26302, 27222 => 26331, 27223 => 26332, 27224 => 26342,
27225 => 26345, 27226 => 36146, 27227 => 36147, 27228 => 36150, 27229 => 36155,
27230 => 36157, 27231 => 36160, 27232 => 36165, 27233 => 36166, 27234 => 36168,
27235 => 36169, 27236 => 36167, 27237 => 36173, 27238 => 36181, 27239 => 36185,
27240 => 35271, 27241 => 35274, 27242 => 35275, 27243 => 35276, 27244 => 35278,
27245 => 35279, 27246 => 35280, 27247 => 35281, 27248 => 29294, 27249 => 29343,
27250 => 29277, 27251 => 29286, 27252 => 29295, 27253 => 29310, 27254 => 29311,
27255 => 29316, 27256 => 29323, 27257 => 29325, 27258 => 29327, 27259 => 29330,
27260 => 25352, 27261 => 25394, 27262 => 25520, 27425 => 25663, 27426 => 25816,
27427 => 32772, 27428 => 27626, 27429 => 27635, 27430 => 27645, 27431 => 27637,
27432 => 27641, 27433 => 27653, 27434 => 27655, 27435 => 27654, 27436 => 27661,
27437 => 27669, 27438 => 27672, 27439 => 27673, 27440 => 27674, 27441 => 27681,
27442 => 27689, 27443 => 27684, 27444 => 27690, 27445 => 27698, 27446 => 25909,
27447 => 25941, 27448 => 25963, 27449 => 29261, 27450 => 29266, 27451 => 29270,
27452 => 29232, 27453 => 34402, 27454 => 21014, 27455 => 32927, 27456 => 32924,
27457 => 32915, 27458 => 32956, 27459 => 26378, 27460 => 32957, 27461 => 32945,
27462 => 32939, 27463 => 32941, 27464 => 32948, 27465 => 32951, 27466 => 32999,
27467 => 33000, 27468 => 33001, 27469 => 33002, 27470 => 32987, 27471 => 32962,
27472 => 32964, 27473 => 32985, 27474 => 32973, 27475 => 32983, 27476 => 26384,
27477 => 32989, 27478 => 33003, 27479 => 33009, 27480 => 33012, 27481 => 33005,
27482 => 33037, 27483 => 33038, 27484 => 33010, 27485 => 33020, 27486 => 26389,
27487 => 33042, 27488 => 35930, 27489 => 33078, 27490 => 33054, 27491 => 33068,
27492 => 33048, 27493 => 33074, 27494 => 33096, 27495 => 33100, 27496 => 33107,
27497 => 33140, 27498 => 33113, 27499 => 33114, 27500 => 33137, 27501 => 33120,
27502 => 33129, 27503 => 33148, 27504 => 33149, 27505 => 33133, 27506 => 33127,
27507 => 22605, 27508 => 23221, 27509 => 33160, 27510 => 33154, 27511 => 33169,
27512 => 28373, 27513 => 33187, 27514 => 33194, 27515 => 33228, 27516 => 26406,
27517 => 33226, 27518 => 33211, 27681 => 33217, 27682 => 33190, 27683 => 27428,
27684 => 27447, 27685 => 27449, 27686 => 27459, 27687 => 27462, 27688 => 27481,
27689 => 39121, 27690 => 39122, 27691 => 39123, 27692 => 39125, 27693 => 39129,
27694 => 39130, 27695 => 27571, 27696 => 24384, 27697 => 27586, 27698 => 35315,
27699 => 26000, 27700 => 40785, 27701 => 26003, 27702 => 26044, 27703 => 26054,
27704 => 26052, 27705 => 26051, 27706 => 26060, 27707 => 26062, 27708 => 26066,
27709 => 26070, 27710 => 28800, 27711 => 28828, 27712 => 28822, 27713 => 28829,
27714 => 28859, 27715 => 28864, 27716 => 28855, 27717 => 28843, 27718 => 28849,
27719 => 28904, 27720 => 28874, 27721 => 28944, 27722 => 28947, 27723 => 28950,
27724 => 28975, 27725 => 28977, 27726 => 29043, 27727 => 29020, 27728 => 29032,
27729 => 28997, 27730 => 29042, 27731 => 29002, 27732 => 29048, 27733 => 29050,
27734 => 29080, 27735 => 29107, 27736 => 29109, 27737 => 29096, 27738 => 29088,
27739 => 29152, 27740 => 29140, 27741 => 29159, 27742 => 29177, 27743 => 29213,
27744 => 29224, 27745 => 28780, 27746 => 28952, 27747 => 29030, 27748 => 29113,
27749 => 25150, 27750 => 25149, 27751 => 25155, 27752 => 25160, 27753 => 25161,
27754 => 31035, 27755 => 31040, 27756 => 31046, 27757 => 31049, 27758 => 31067,
27759 => 31068, 27760 => 31059, 27761 => 31066, 27762 => 31074, 27763 => 31063,
27764 => 31072, 27765 => 31087, 27766 => 31079, 27767 => 31098, 27768 => 31109,
27769 => 31114, 27770 => 31130, 27771 => 31143, 27772 => 31155, 27773 => 24529,
27774 => 24528, 27937 => 24636, 27938 => 24669, 27939 => 24666, 27940 => 24679,
27941 => 24641, 27942 => 24665, 27943 => 24675, 27944 => 24747, 27945 => 24838,
27946 => 24845, 27947 => 24925, 27948 => 25001, 27949 => 24989, 27950 => 25035,
27951 => 25041, 27952 => 25094, 27953 => 32896, 27954 => 32895, 27955 => 27795,
27956 => 27894, 27957 => 28156, 27958 => 30710, 27959 => 30712, 27960 => 30720,
27961 => 30729, 27962 => 30743, 27963 => 30744, 27964 => 30737, 27965 => 26027,
27966 => 30765, 27967 => 30748, 27968 => 30749, 27969 => 30777, 27970 => 30778,
27971 => 30779, 27972 => 30751, 27973 => 30780, 27974 => 30757, 27975 => 30764,
27976 => 30755, 27977 => 30761, 27978 => 30798, 27979 => 30829, 27980 => 30806,
27981 => 30807, 27982 => 30758, 27983 => 30800, 27984 => 30791, 27985 => 30796,
27986 => 30826, 27987 => 30875, 27988 => 30867, 27989 => 30874, 27990 => 30855,
27991 => 30876, 27992 => 30881, 27993 => 30883, 27994 => 30898, 27995 => 30905,
27996 => 30885, 27997 => 30932, 27998 => 30937, 27999 => 30921, 28000 => 30956,
28001 => 30962, 28002 => 30981, 28003 => 30964, 28004 => 30995, 28005 => 31012,
28006 => 31006, 28007 => 31028, 28008 => 40859, 28009 => 40697, 28010 => 40699,
28011 => 40700, 28012 => 30449, 28013 => 30468, 28014 => 30477, 28015 => 30457,
28016 => 30471, 28017 => 30472, 28018 => 30490, 28019 => 30498, 28020 => 30489,
28021 => 30509, 28022 => 30502, 28023 => 30517, 28024 => 30520, 28025 => 30544,
28026 => 30545, 28027 => 30535, 28028 => 30531, 28029 => 30554, 28030 => 30568,
28193 => 30562, 28194 => 30565, 28195 => 30591, 28196 => 30605, 28197 => 30589,
28198 => 30592, 28199 => 30604, 28200 => 30609, 28201 => 30623, 28202 => 30624,
28203 => 30640, 28204 => 30645, 28205 => 30653, 28206 => 30010, 28207 => 30016,
28208 => 30030, 28209 => 30027, 28210 => 30024, 28211 => 30043, 28212 => 30066,
28213 => 30073, 28214 => 30083, 28215 => 32600, 28216 => 32609, 28217 => 32607,
28218 => 35400, 28219 => 32616, 28220 => 32628, 28221 => 32625, 28222 => 32633,
28223 => 32641, 28224 => 32638, 28225 => 30413, 28226 => 30437, 28227 => 34866,
28228 => 38021, 28229 => 38022, 28230 => 38023, 28231 => 38027, 28232 => 38026,
28233 => 38028, 28234 => 38029, 28235 => 38031, 28236 => 38032, 28237 => 38036,
28238 => 38039, 28239 => 38037, 28240 => 38042, 28241 => 38043, 28242 => 38044,
28243 => 38051, 28244 => 38052, 28245 => 38059, 28246 => 38058, 28247 => 38061,
28248 => 38060, 28249 => 38063, 28250 => 38064, 28251 => 38066, 28252 => 38068,
28253 => 38070, 28254 => 38071, 28255 => 38072, 28256 => 38073, 28257 => 38074,
28258 => 38076, 28259 => 38077, 28260 => 38079, 28261 => 38084, 28262 => 38088,
28263 => 38089, 28264 => 38090, 28265 => 38091, 28266 => 38092, 28267 => 38093,
28268 => 38094, 28269 => 38096, 28270 => 38097, 28271 => 38098, 28272 => 38101,
28273 => 38102, 28274 => 38103, 28275 => 38105, 28276 => 38104, 28277 => 38107,
28278 => 38110, 28279 => 38111, 28280 => 38112, 28281 => 38114, 28282 => 38116,
28283 => 38117, 28284 => 38119, 28285 => 38120, 28286 => 38122, 28449 => 38121,
28450 => 38123, 28451 => 38126, 28452 => 38127, 28453 => 38131, 28454 => 38132,
28455 => 38133, 28456 => 38135, 28457 => 38137, 28458 => 38140, 28459 => 38141,
28460 => 38143, 28461 => 38147, 28462 => 38146, 28463 => 38150, 28464 => 38151,
28465 => 38153, 28466 => 38154, 28467 => 38157, 28468 => 38158, 28469 => 38159,
28470 => 38162, 28471 => 38163, 28472 => 38164, 28473 => 38165, 28474 => 38166,
28475 => 38168, 28476 => 38171, 28477 => 38173, 28478 => 38174, 28479 => 38175,
28480 => 38178, 28481 => 38186, 28482 => 38187, 28483 => 38185, 28484 => 38188,
28485 => 38193, 28486 => 38194, 28487 => 38196, 28488 => 38198, 28489 => 38199,
28490 => 38200, 28491 => 38204, 28492 => 38206, 28493 => 38207, 28494 => 38210,
28495 => 38197, 28496 => 38212, 28497 => 38213, 28498 => 38214, 28499 => 38217,
28500 => 38220, 28501 => 38222, 28502 => 38223, 28503 => 38226, 28504 => 38227,
28505 => 38228, 28506 => 38230, 28507 => 38231, 28508 => 38232, 28509 => 38233,
28510 => 38235, 28511 => 38238, 28512 => 38239, 28513 => 38237, 28514 => 38241,
28515 => 38242, 28516 => 38244, 28517 => 38245, 28518 => 38246, 28519 => 38247,
28520 => 38248, 28521 => 38249, 28522 => 38250, 28523 => 38251, 28524 => 38252,
28525 => 38255, 28526 => 38257, 28527 => 38258, 28528 => 38259, 28529 => 38202,
28530 => 30695, 28531 => 30700, 28532 => 38601, 28533 => 31189, 28534 => 31213,
28535 => 31203, 28536 => 31211, 28537 => 31238, 28538 => 23879, 28539 => 31235,
28540 => 31234, 28541 => 31262, 28542 => 31252, 28705 => 31289, 28706 => 31287,
28707 => 31313, 28708 => 40655, 28709 => 39333, 28710 => 31344, 28711 => 30344,
28712 => 30350, 28713 => 30355, 28714 => 30361, 28715 => 30372, 28716 => 29918,
28717 => 29920, 28718 => 29996, 28719 => 40480, 28720 => 40482, 28721 => 40488,
28722 => 40489, 28723 => 40490, 28724 => 40491, 28725 => 40492, 28726 => 40498,
28727 => 40497, 28728 => 40502, 28729 => 40504, 28730 => 40503, 28731 => 40505,
28732 => 40506, 28733 => 40510, 28734 => 40513, 28735 => 40514, 28736 => 40516,
28737 => 40518, 28738 => 40519, 28739 => 40520, 28740 => 40521, 28741 => 40523,
28742 => 40524, 28743 => 40526, 28744 => 40529, 28745 => 40533, 28746 => 40535,
28747 => 40538, 28748 => 40539, 28749 => 40540, 28750 => 40542, 28751 => 40547,
28752 => 40550, 28753 => 40551, 28754 => 40552, 28755 => 40553, 28756 => 40554,
28757 => 40555, 28758 => 40556, 28759 => 40561, 28760 => 40557, 28761 => 40563,
28762 => 30098, 28763 => 30100, 28764 => 30102, 28765 => 30112, 28766 => 30109,
28767 => 30124, 28768 => 30115, 28769 => 30131, 28770 => 30132, 28771 => 30136,
28772 => 30148, 28773 => 30129, 28774 => 30128, 28775 => 30147, 28776 => 30146,
28777 => 30166, 28778 => 30157, 28779 => 30179, 28780 => 30184, 28781 => 30182,
28782 => 30180, 28783 => 30187, 28784 => 30183, 28785 => 30211, 28786 => 30193,
28787 => 30204, 28788 => 30207, 28789 => 30224, 28790 => 30208, 28791 => 30213,
28792 => 30220, 28793 => 30231, 28794 => 30218, 28795 => 30245, 28796 => 30232,
28797 => 30229, 28798 => 30233, 28961 => 30235, 28962 => 30268, 28963 => 30242,
28964 => 30240, 28965 => 30272, 28966 => 30253, 28967 => 30256, 28968 => 30271,
28969 => 30261, 28970 => 30275, 28971 => 30270, 28972 => 30259, 28973 => 30285,
28974 => 30302, 28975 => 30292, 28976 => 30300, 28977 => 30294, 28978 => 30315,
28979 => 30319, 28980 => 32714, 28981 => 31462, 28982 => 31352, 28983 => 31353,
28984 => 31360, 28985 => 31366, 28986 => 31368, 28987 => 31381, 28988 => 31398,
28989 => 31392, 28990 => 31404, 28991 => 31400, 28992 => 31405, 28993 => 31411,
28994 => 34916, 28995 => 34921, 28996 => 34930, 28997 => 34941, 28998 => 34943,
28999 => 34946, 29000 => 34978, 29001 => 35014, 29002 => 34999, 29003 => 35004,
29004 => 35017, 29005 => 35042, 29006 => 35022, 29007 => 35043, 29008 => 35045,
29009 => 35057, 29010 => 35098, 29011 => 35068, 29012 => 35048, 29013 => 35070,
29014 => 35056, 29015 => 35105, 29016 => 35097, 29017 => 35091, 29018 => 35099,
29019 => 35082, 29020 => 35124, 29021 => 35115, 29022 => 35126, 29023 => 35137,
29024 => 35174, 29025 => 35195, 29026 => 30091, 29027 => 32997, 29028 => 30386,
29029 => 30388, 29030 => 30684, 29031 => 32786, 29032 => 32788, 29033 => 32790,
29034 => 32796, 29035 => 32800, 29036 => 32802, 29037 => 32805, 29038 => 32806,
29039 => 32807, 29040 => 32809, 29041 => 32808, 29042 => 32817, 29043 => 32779,
29044 => 32821, 29045 => 32835, 29046 => 32838, 29047 => 32845, 29048 => 32850,
29049 => 32873, 29050 => 32881, 29051 => 35203, 29052 => 39032, 29053 => 39040,
29054 => 39043, 29217 => 39049, 29218 => 39052, 29219 => 39053, 29220 => 39055,
29221 => 39060, 29222 => 39066, 29223 => 39067, 29224 => 39070, 29225 => 39071,
29226 => 39073, 29227 => 39074, 29228 => 39077, 29229 => 39078, 29230 => 34381,
29231 => 34388, 29232 => 34412, 29233 => 34414, 29234 => 34431, 29235 => 34426,
29236 => 34428, 29237 => 34427, 29238 => 34472, 29239 => 34445, 29240 => 34443,
29241 => 34476, 29242 => 34461, 29243 => 34471, 29244 => 34467, 29245 => 34474,
29246 => 34451, 29247 => 34473, 29248 => 34486, 29249 => 34500, 29250 => 34485,
29251 => 34510, 29252 => 34480, 29253 => 34490, 29254 => 34481, 29255 => 34479,
29256 => 34505, 29257 => 34511, 29258 => 34484, 29259 => 34537, 29260 => 34545,
29261 => 34546, 29262 => 34541, 29263 => 34547, 29264 => 34512, 29265 => 34579,
29266 => 34526, 29267 => 34548, 29268 => 34527, 29269 => 34520, 29270 => 34513,
29271 => 34563, 29272 => 34567, 29273 => 34552, 29274 => 34568, 29275 => 34570,
29276 => 34573, 29277 => 34569, 29278 => 34595, 29279 => 34619, 29280 => 34590,
29281 => 34597, 29282 => 34606, 29283 => 34586, 29284 => 34622, 29285 => 34632,
29286 => 34612, 29287 => 34609, 29288 => 34601, 29289 => 34615, 29290 => 34623,
29291 => 34690, 29292 => 34594, 29293 => 34685, 29294 => 34686, 29295 => 34683,
29296 => 34656, 29297 => 34672, 29298 => 34636, 29299 => 34670, 29300 => 34699,
29301 => 34643, 29302 => 34659, 29303 => 34684, 29304 => 34660, 29305 => 34649,
29306 => 34661, 29307 => 34707, 29308 => 34735, 29309 => 34728, 29310 => 34770,
29473 => 34758, 29474 => 34696, 29475 => 34693, 29476 => 34733, 29477 => 34711,
29478 => 34691, 29479 => 34731, 29480 => 34789, 29481 => 34732, 29482 => 34741,
29483 => 34739, 29484 => 34763, 29485 => 34771, 29486 => 34749, 29487 => 34769,
29488 => 34752, 29489 => 34762, 29490 => 34779, 29491 => 34794, 29492 => 34784,
29493 => 34798, 29494 => 34838, 29495 => 34835, 29496 => 34814, 29497 => 34826,
29498 => 34843, 29499 => 34849, 29500 => 34873, 29501 => 34876, 29502 => 32566,
29503 => 32578, 29504 => 32580, 29505 => 32581, 29506 => 33296, 29507 => 31482,
29508 => 31485, 29509 => 31496, 29510 => 31491, 29511 => 31492, 29512 => 31509,
29513 => 31498, 29514 => 31531, 29515 => 31503, 29516 => 31559, 29517 => 31544,
29518 => 31530, 29519 => 31513, 29520 => 31534, 29521 => 31537, 29522 => 31520,
29523 => 31525, 29524 => 31524, 29525 => 31539, 29526 => 31550, 29527 => 31518,
29528 => 31576, 29529 => 31578, 29530 => 31557, 29531 => 31605, 29532 => 31564,
29533 => 31581, 29534 => 31584, 29535 => 31598, 29536 => 31611, 29537 => 31586,
29538 => 31602, 29539 => 31601, 29540 => 31632, 29541 => 31654, 29542 => 31655,
29543 => 31672, 29544 => 31660, 29545 => 31645, 29546 => 31656, 29547 => 31621,
29548 => 31658, 29549 => 31644, 29550 => 31650, 29551 => 31659, 29552 => 31668,
29553 => 31697, 29554 => 31681, 29555 => 31692, 29556 => 31709, 29557 => 31706,
29558 => 31717, 29559 => 31718, 29560 => 31722, 29561 => 31756, 29562 => 31742,
29563 => 31740, 29564 => 31759, 29565 => 31766, 29566 => 31755, 29729 => 31775,
29730 => 31786, 29731 => 31782, 29732 => 31800, 29733 => 31809, 29734 => 31808,
29735 => 33278, 29736 => 33281, 29737 => 33282, 29738 => 33284, 29739 => 33260,
29740 => 34884, 29741 => 33313, 29742 => 33314, 29743 => 33315, 29744 => 33325,
29745 => 33327, 29746 => 33320, 29747 => 33323, 29748 => 33336, 29749 => 33339,
29750 => 33331, 29751 => 33332, 29752 => 33342, 29753 => 33348, 29754 => 33353,
29755 => 33355, 29756 => 33359, 29757 => 33370, 29758 => 33375, 29759 => 33384,
29760 => 34942, 29761 => 34949, 29762 => 34952, 29763 => 35032, 29764 => 35039,
29765 => 35166, 29766 => 32669, 29767 => 32671, 29768 => 32679, 29769 => 32687,
29770 => 32688, 29771 => 32690, 29772 => 31868, 29773 => 25929, 29774 => 31889,
29775 => 31901, 29776 => 31900, 29777 => 31902, 29778 => 31906, 29779 => 31922,
29780 => 31932, 29781 => 31933, 29782 => 31937, 29783 => 31943, 29784 => 31948,
29785 => 31949, 29786 => 31944, 29787 => 31941, 29788 => 31959, 29789 => 31976,
29790 => 33390, 29791 => 26280, 29792 => 32703, 29793 => 32718, 29794 => 32725,
29795 => 32741, 29796 => 32737, 29797 => 32742, 29798 => 32745, 29799 => 32750,
29800 => 32755, 29801 => 31992, 29802 => 32119, 29803 => 32166, 29804 => 32174,
29805 => 32327, 29806 => 32411, 29807 => 40632, 29808 => 40628, 29809 => 36211,
29810 => 36228, 29811 => 36244, 29812 => 36241, 29813 => 36273, 29814 => 36199,
29815 => 36205, 29816 => 35911, 29817 => 35913, 29818 => 37194, 29819 => 37200,
29820 => 37198, 29821 => 37199, 29822 => 37220, 29985 => 37218, 29986 => 37217,
29987 => 37232, 29988 => 37225, 29989 => 37231, 29990 => 37245, 29991 => 37246,
29992 => 37234, 29993 => 37236, 29994 => 37241, 29995 => 37260, 29996 => 37253,
29997 => 37264, 29998 => 37261, 29999 => 37265, 30000 => 37282, 30001 => 37283,
30002 => 37290, 30003 => 37293, 30004 => 37294, 30005 => 37295, 30006 => 37301,
30007 => 37300, 30008 => 37306, 30009 => 35925, 30010 => 40574, 30011 => 36280,
30012 => 36331, 30013 => 36357, 30014 => 36441, 30015 => 36457, 30016 => 36277,
30017 => 36287, 30018 => 36284, 30019 => 36282, 30020 => 36292, 30021 => 36310,
30022 => 36311, 30023 => 36314, 30024 => 36318, 30025 => 36302, 30026 => 36303,
30027 => 36315, 30028 => 36294, 30029 => 36332, 30030 => 36343, 30031 => 36344,
30032 => 36323, 30033 => 36345, 30034 => 36347, 30035 => 36324, 30036 => 36361,
30037 => 36349, 30038 => 36372, 30039 => 36381, 30040 => 36383, 30041 => 36396,
30042 => 36398, 30043 => 36387, 30044 => 36399, 30045 => 36410, 30046 => 36416,
30047 => 36409, 30048 => 36405, 30049 => 36413, 30050 => 36401, 30051 => 36425,
30052 => 36417, 30053 => 36418, 30054 => 36433, 30055 => 36434, 30056 => 36426,
30057 => 36464, 30058 => 36470, 30059 => 36476, 30060 => 36463, 30061 => 36468,
30062 => 36485, 30063 => 36495, 30064 => 36500, 30065 => 36496, 30066 => 36508,
30067 => 36510, 30068 => 35960, 30069 => 35970, 30070 => 35978, 30071 => 35973,
30072 => 35992, 30073 => 35988, 30074 => 26011, 30075 => 35286, 30076 => 35294,
30077 => 35290, 30078 => 35292, 30241 => 35301, 30242 => 35307, 30243 => 35311,
30244 => 35390, 30245 => 35622, 30246 => 38739, 30247 => 38633, 30248 => 38643,
30249 => 38639, 30250 => 38662, 30251 => 38657, 30252 => 38664, 30253 => 38671,
30254 => 38670, 30255 => 38698, 30256 => 38701, 30257 => 38704, 30258 => 38718,
30259 => 40832, 30260 => 40835, 30261 => 40837, 30262 => 40838, 30263 => 40839,
30264 => 40840, 30265 => 40841, 30266 => 40842, 30267 => 40844, 30268 => 40702,
30269 => 40715, 30270 => 40717, 30271 => 38585, 30272 => 38588, 30273 => 38589,
30274 => 38606, 30275 => 38610, 30276 => 30655, 30277 => 38624, 30278 => 37518,
30279 => 37550, 30280 => 37576, 30281 => 37694, 30282 => 37738, 30283 => 37834,
30284 => 37775, 30285 => 37950, 30286 => 37995, 30287 => 40063, 30288 => 40066,
30289 => 40069, 30290 => 40070, 30291 => 40071, 30292 => 40072, 30293 => 31267,
30294 => 40075, 30295 => 40078, 30296 => 40080, 30297 => 40081, 30298 => 40082,
30299 => 40084, 30300 => 40085, 30301 => 40090, 30302 => 40091, 30303 => 40094,
30304 => 40095, 30305 => 40096, 30306 => 40097, 30307 => 40098, 30308 => 40099,
30309 => 40101, 30310 => 40102, 30311 => 40103, 30312 => 40104, 30313 => 40105,
30314 => 40107, 30315 => 40109, 30316 => 40110, 30317 => 40112, 30318 => 40113,
30319 => 40114, 30320 => 40115, 30321 => 40116, 30322 => 40117, 30323 => 40118,
30324 => 40119, 30325 => 40122, 30326 => 40123, 30327 => 40124, 30328 => 40125,
30329 => 40132, 30330 => 40133, 30331 => 40134, 30332 => 40135, 30333 => 40138,
30334 => 40139, 30497 => 40140, 30498 => 40141, 30499 => 40142, 30500 => 40143,
30501 => 40144, 30502 => 40147, 30503 => 40148, 30504 => 40149, 30505 => 40151,
30506 => 40152, 30507 => 40153, 30508 => 40156, 30509 => 40157, 30510 => 40159,
30511 => 40162, 30512 => 38780, 30513 => 38789, 30514 => 38801, 30515 => 38802,
30516 => 38804, 30517 => 38831, 30518 => 38827, 30519 => 38819, 30520 => 38834,
30521 => 38836, 30522 => 39601, 30523 => 39600, 30524 => 39607, 30525 => 40536,
30526 => 39606, 30527 => 39610, 30528 => 39612, 30529 => 39617, 30530 => 39616,
30531 => 39621, 30532 => 39618, 30533 => 39627, 30534 => 39628, 30535 => 39633,
30536 => 39749, 30537 => 39747, 30538 => 39751, 30539 => 39753, 30540 => 39752,
30541 => 39757, 30542 => 39761, 30543 => 39144, 30544 => 39181, 30545 => 39214,
30546 => 39253, 30547 => 39252, 30548 => 39647, 30549 => 39649, 30550 => 39654,
30551 => 39663, 30552 => 39659, 30553 => 39675, 30554 => 39661, 30555 => 39673,
30556 => 39688, 30557 => 39695, 30558 => 39699, 30559 => 39711, 30560 => 39715,
30561 => 40637, 30562 => 40638, 30563 => 32315, 30564 => 40578, 30565 => 40583,
30566 => 40584, 30567 => 40587, 30568 => 40594, 30569 => 37846, 30570 => 40605,
30571 => 40607, 30572 => 40667, 30573 => 40668, 30574 => 40669, 30575 => 40672,
30576 => 40671, 30577 => 40674, 30578 => 40681, 30579 => 40679, 30580 => 40677,
30581 => 40682, 30582 => 40687, 30583 => 40738, 30584 => 40748, 30585 => 40751,
30586 => 40761, 30587 => 40759, 30588 => 40765, 30589 => 40766, 30590 => 40772,
0 => 0 );
 
function gb2utf8($gb) {
if( !trim($gb) ) return $gb;
$utf8='';
while($gb) {
if( ord(substr($gb,0,1)) > 127 ) {
$t=substr($gb,0,2);
$gb=substr($gb,2);
$utf8 .= $this->u2utf8($this->codetable[hexdec(bin2hex($t))-0x8080]);
}
else {
$t=substr($gb,0,1);
$gb=substr($gb,1);
$utf8 .= $this->u2utf8($t);
}
}
return $utf8;
}
function u2utf8($c) {
$str='';
if ($c < 0x80) {
$str.=$c;
}
else if ($c < 0x800) {
$str.=chr(0xC0 | $c>>6);
$str.=chr(0x80 | $c & 0x3F);
}
else if ($c < 0x10000) {
$str.=chr(0xE0 | $c>>12);
$str.=chr(0x80 | $c>>6 & 0x3F);
$str.=chr(0x80 | $c & 0x3F);
}
else if ($c < 0x200000) {
$str.=chr(0xF0 | $c>>18);
$str.=chr(0x80 | $c>>12 & 0x3F);
$str.=chr(0x80 | $c>>6 & 0x3F);
$str.=chr(0x80 | $c & 0x3F);
}
return $str;
}
 
} // END Class
 
?>