php控制写图片的类库文件

下面为php版的写jpg类型图片文件类库:

[@more@]

<?php
/*=======================================================================
// File: JPGRAPH.PHP
// Description: PHP4 Graph Plotting library. Base module.
// Created: 2001-01-08
// Author: Johan Persson (johanp@aditus.nu)
// Ver: jpgraph.inc.php,v 1.1.1.1 2004/07/09 08:55:24 administrator Exp
//
// License: This code is released under GPL 2.0
//
//========================================================================
*/

//------------------------------------------------------------------
// Manifest Constants that control varius aspect of JpGraph
//------------------------------------------------------------------
// The full absolute name of directory to be used as a cache. This directory MUST
// be readable and writable for PHP. Must end with '/'
DEFINE("CACHE_DIR","/tmp/jpgcache/");

// The URL relative name where the cache can be found, i.e
// under what HTTP directory can the cache be found. Normally
// you would probably assign an alias in apache configuration
// for the cache directory.
DEFINE("APACHE_CACHE_DIR","/jpgcache/");

// Directory for TTF fonts. Must end with '/'
DEFINE("TTF_DIR","/usr/local/ttf/");

// What grou 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","nobody");

// What permissions should the cached file have
// (Set to "" will give the default persmissions for the "PHP-user")
DEFINE("CACHE_FILE_MOD",0774);

// Should we try to read from the cache? Set to false to bypass the
// reading of the cache and always re-generate the image and save it in
// the cache. Useful for debugging.
DEFINE("READ_CACHE",false);

// 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);

// If the color palette is full should JpGraph try to allocate
// the closest match? If you plan on using background image 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.
DEFINE("USE_APPROX_COLORS",true);

// 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);
DEFINE("BRAND_TIME_FORMAT","Generated in: %01.3fs");

// 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);

// Deafult graphic format set to "auto" which will automtically
// 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");

// Enable some extra debug information. Should only be enabled if you know
// what you are doing!
DEFINE("JPG_DEBUG",false);

//------------------------------------------------------------------
// 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_HANDWRT",13);
DEFINE("FF_COMIC",14);
DEFINE("FF_ARIAL",15);
DEFINE("FF_BOOK",16);

// TTF Font styles
DEFINE("FS_NORMAL",1);
DEFINE("FS_BOLD",2);
DEFINE("FS_ITALIC",3);
DEFINE("FS_BOLDIT",4);

//Definitions for internal font, old style
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

//Definitions for internal font, new style
DEFINE("FF_FONT0",1);
DEFINE("FF_FONT1",2);
DEFINE("FF_FONT2",4);

// 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_UP",1);

// Legend type stacked vertical or horizontal
DEFINE("LEGEND_VERT",0);
DEFINE("LEGEND_HOR",1);

// Mark types
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);

// Styles for gradient color fill
DEFINE("GRAD_VER",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);

// 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);

// Depth
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_LVERT",4); // Vertical lines
DEFINE("BAND_LHOR",5); // Horizontal lines
DEFINE("BAND_3DPLANE",6); // "3D" Plane
DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses

// Usefull mathematical function
function sign($a) {if( $a>=0) return 1; else return -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 $PHP_SELF;
$supported = imagetypes();
if( $supported & IMG_PNG )
$img_format="png";
elseif( $supported & IMG_GIF )
$img_format="gif";
elseif( $supported & IMG_JPG )
$img_format="jpg";
$fname=basename($PHP_SELF);
return substr($fname,0,strlen($fname)-4).".".$img_format;
}


//===================================================
// 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


//===================================================
// 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(198,198,198); // Margin coor of graph
var $plotarea_color=array(255,255,255); // Plot area color
var $title,$subtitle; // Title and subtitle 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
//---------------
// 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) {

// If timing is used create a new timing object
if( BRAND_TIMING ) {
global $tim;
$tim = new JpgTimer();
$tim->Push();
}

// Automtically 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->subtitle = new Text("");
$this->legend = new Legend();

// 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
}
//---------------
// PUBLIC METHODS

function SetGridDepth($aDepth) {
$this->grid_depth=$aDepth;
}

// Specify graph angle 0-360 degrees.
function SetAngle($aAngle) {
$this->img->SetAngle($aAngle);
}

// Add a plot object to the graph
function Add(&$aPlot) {
if( $aPlot == null )
die("JpGraph Error: Graph::Add() You tried to add a null plot to the graph.");
$this->plots[] = $aPlot;
}

// Add plot to second Y-scale
function AddY2(&$aPlot) {
if( $aPlot == null )
die("JpGraph Error: 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 )
die("JpGraph Error: Graph::AddText() You tried to add a null text to the graph.");
if( is_array($aTxt) ) {
for($i=0; $i $this->texts[]=$aTxt[$i];
}
else
$this->texts[] = $aTxt;
}

// Add a line object (class PlotLine) to the graph
function AddLine(&$aLine) {
if( $aLine == null )
die("JpGraph Error: Graph::AddLine() You tried to add a null line to the graph.");
if( is_array($aLine) ) {
for($i=0; $i $this->lines[]=$aLine[$i];
}
else
$this->lines[] = $aLine;
}

// Add vertical or horizontal band
function AddBand(&$aBand) {
if( $aBand == null )
die("JpGraph Error: Graph::AddBand() You tried to add a null band to the graph.");
if( is_array($aBand) ) {
for($i=0; $i $this->bands[]=$aBand[$i];
}
else
$this->bands[]=$aBand;
}


// Specify a background image
function SetBackgroundImage($aFileName,$aBgType=1,$aImgFormat="png") {
$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;
}

// 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 marginals)
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;
}

// 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;
}

// 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;

$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
die("JpGraph Error: Unknown scale specification for Y-scale. ($axtype)");

$xt=substr($aAxisType,0,3);
if( $xt == "lin" || $xt == "tex" )
$this->xscale = new LinearScale($aXMin,$aXMax,"x");
elseif( $xt == "int" ) {
$this->xscale = new LinearScale($aXMin,$aXMax,"x");
$this->xscale->SetIntScale();
}
elseif( $xt == "log" )
$this->xscale = new LogScale($aXMin,$aXMax,"x");
else
die("JpGraph Error: 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=="log" ) {
$this->y2scale = new LogScale($aY2Min,$aY2Max);
}
else die("JpGraph: Unsupported Y2 axis type: $axtype
");

$this->y2scale->Init($this->img);
$this->y2axis = new Axis($this->img,$this->y2scale);
$this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
$this->y2axis->SetLabelPos(SIDE_RIGHT);

// Deafult position is the max x-value
$this->y2axis->SetPos($this->xscale->GetMaxVal());
$this->y2grid = new Grid($this->y2axis);
}

// Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
// The dividing factor have been determined heuristic according to my aesthetic sense
// 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:
die("JpGraph: Unsupported Tick density: $densy");
}
switch( $aXDensity ) {
case TICKD_DENSE:
$this->xtick_factor=18;
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:
die("JpGraph: Unsupported Tick density: $densx");
}
}

// Get a string of all image map areas
function GetCSIMareas() {
$csim="";
foreach ($this->plots as $p) {
$csim.= $p->GetCSIMareas();
}
return $csim;
}

// Get a complete .. tag for the final image map
function GetHTMLImageMap($aMapName) {
$im = " ";
$im .= $this->GetCSIMareas();
$im .= "";
return $im;
}


// Stroke the graph
// $aStrokeFileName If != "" the image will be written to this file and NOT
// streamed back to the browser
function Stroke($aStrokeFileName="") {

// 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; $iplots) ; ++$i ) {
$this->plots[$i]->PreStrokeAdjust($this);
$this->plots[$i]->Legend($this);
}

if( $this->y2scale != null ) {
for($i=0; $iy2plots) ; ++$i ) {
$this->y2plots[$i]->PreStrokeAdjust($this);
$this->y2plots[$i]->Legend($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) ) {
die("JpGraph: Can't draw unspecified Y-scale.

You have either:

* Specified an Y axis for autoscaling but have not supplied any plots

* Specified a scale manually but have forgot to specify the tick steps");
}

// Bail out if no plots and no specified X-scale
if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
die("JpGraph: Can't draw unspecified X-scale.
No plots.
");

//Check if we should autoscale y-axis
if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
list($min,$max) = $this->GetPlotsYMinMax($this->plots);
$this->yscale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
}

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);
}

//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;
$this->xscale->Update($this->img,$min,$max);
$this->xscale->ticks->Set($this->xaxis->tick_step,1);
$this->xscale->ticks->SupressMinorTickMarks();
}
else {
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);
}
$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
$this->yaxis->SetPos($this->xscale->GetMinVal());
if( $this->y2axis != null ) {
$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.

if( !$this->xaxis->pos && $this->yscale->GetMinVal() < 0 ) {
$this->yscale->ticks->SupressZeroLabel(false);
$this->xscale->ticks->SupressFirst();
if( $this->y2axis != null ) {
$this->xscale->ticks->SupressLast();
}
}

// Copy in background image
if( $this->background_image != "" ) {
$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);

$aa = $this->img->SetAngle(0);

switch( $this->background_image_type ) {
case BGIMG_FILLPLOT: // Resize to just fill the plotarea
$this->StrokeFrame();
imagecopyresized($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
imagecopyresized($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
imagecopyresized($this->img->img,$bkgimg,
0,0,0,0,
$bw,$bh,
$bw,$bh);
$this->StrokeFrame();
break;
default:
die("JpGraph Error: Unknown background image layout");
}
$this->img->SetAngle($aa);
}
else {
$aa = $this->img->SetAngle(0);
$this->StrokeFrame();
$this->img->SetAngle($aa);

$this->img->SetColor($this->plotarea_color);
$this->img->FilledRectangle($this->img->left_margin,
$this->img->top_margin,
$this->img->width-$this->img->right_margin,
$this->img->height-$this->img->bottom_margin);
}


// Stroke axis
$this->xaxis->Stroke($this->yscale);
$this->yaxis->Stroke($this->xscale);

// Stroke bands
if( $this->bands != null )
for($i=0; $ibands); ++$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 ) {
$this->ygrid->Stroke();
$this->xgrid->Stroke();
}

// Stroke Y2-axis
if( $this->y2axis != null ) {
$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);
}

// Stroke all plots for Y1 axis
for($i=0; $iplots) ; ++$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);
}

$this->xscale->off=$oldoff;

// Stroke bands
if( $this->bands!= null )
for($i=0; $ibands); ++$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);
}

if( $this->grid_depth == DEPTH_FRONT ) {
$this->ygrid->Stroke();
$this->xgrid->Stroke();
}

// Finally draw the axis again since some plots may have nagged
// the axis in the edges.
$this->yaxis->Stroke($this->xscale);
$this->xaxis->Stroke($this->yscale);

if( $this->y2scale != null)
$this->y2axis->Stroke($this->xscale);

// 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);
}

$aa = $this->img->SetAngle(0);

// Stroke title
$this->title->Center($this->img->left_margin,$this->img->width-$this->img->right_margin,5);
$this->title->Stroke($this->img);

// ... and subtitle
$this->subtitle->Center($this->img->left_margin,$this->img->width-$this->img->right_margin,
7+$this->title->GetFontHeight($this->img));
$this->subtitle->Stroke($this->img);


// Stroke legend
$this->legend->Stroke($this->img);

// Stroke any user added text objects

if( $this->texts != null ) {
for($i=0; $itexts); ++$i) {
$this->texts[$i]->x *= $this->img->width;
$this->texts[$i]->y *= $this->img->height;
$this->texts[$i]->Stroke($this->img);
}
}
$this->img->SetAngle($aa);

// Stroke any lines added
if( $this->lines != null ) {
for($i=0; $ilines); ++$i) {
$this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
}
}


if ($this->showcsim) { // 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 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 $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]);

}
}
}
}
// Adjust the brightness and contrast
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);

// Finally stream the generated picture
$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName);
}

//---------------
// PRIVATE METHODS
// Private helper function for backgound image
function LoadBkgImage($aImgFormat="png",$aBright=0,$aContr=0) {
$f = "imagecreatefrom".$aImgFormat;
$img = @$f($this->background_image);
if( !$img ) {
die("JpGraph Error: Can't read background image: '".$this->background_image."'");
}
return $img;
}

// Text scale offset in world coordinates
function SetTextScaleOff($aOff) {
$this->text_scale_off = $aOff;
}

// 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 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);
}

// Draw a frame around the image
function StrokeFrame() {
if( !$this->doframe ) return;
if( $this->doshadow ) {
$this->img->SetColor($this->frame_color);
if( $this->background_image_type <= 1 )
$c = $this->margin_color;
else
$c = false;
$this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height,
$c,$this->shadow_width);
}
else {
$this->img->SetLineWeight($this->frame_weight);
if( $this->background_image_type <= 1 ) {
$this->img->SetColor($this->margin_color);
$this->img->FilledRectangle(1,1,$this->img->width-2,$this->img->height-2);
}
$this->img->SetColor($this->frame_color);
$this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
}
}
} // Class


//===================================================
// CLASS TTF
// Description: Handle TTF font names
//===================================================
class TTF {
var $font_fam;
//---------------
// CONSTRUCTOR
function TTF() {
// Base file names for available fonts
$this->font_fam=array(
FF_COURIER => TTF_DIR."courier",
FF_VERDANA => TTF_DIR."verdana",
FF_TIMES => TTF_DIR."times",
FF_HANDWRT => TTF_DIR."handwriting",
FF_COMIC => TTF_DIR."comic",
FF_ARIAL => TTF_DIR."arial",
FF_BOOK => TTF_DIR."bookant");
}

//---------------
// PUBLIC METHODS
// Create the TTF file from the font specification
function File($fam,$style=FS_NORMAL) {
$f=$this->font_fam[$fam];
if( !$f ) die("JpGraph Error: Unknown TTF font family.");
switch( $style ) {
case FS_NORMAL:
break;
case FS_BOLD: $f .= "bd";
break;
case FS_ITALIC: $f .= "i";
break;
case FS_BOLDIT: $f .= "bi";
break;
default:
die("JpGraph Error: Unknown TTF Style.");
}
$f .= ".ttf";
return $f;
}
} // Class


//===================================================
// 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 $size=2,$font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$hide=false,$dir=0;
var $boxed=false; // Should the text be boxed
//---------------
// CONSTRUCTOR

// Create new text at absolute pixel coordinates
function Text($aTxt="",$aXAbsPos=0,$aYAbsPos=0) {
$this->t = $aTxt;
$this->x = $aXAbsPos;
$this->y = $aYAbsPos;
}
//---------------
// PUBLIC METHODS
// Set the string in theb text object
function Set($aTxt) {
$this->t = $aTxt;
}

// 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;
}

// Specify alignment for the text
function Align($aHAlign,$aVAlign="top") {
$this->halign = $aHAlign;
$this->valign = $aVAlign;
}

// 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),$aShadow=false) {
if( $aFrameColor==false )
$this->boxed=false;
else
$this->boxed=true;
$this->fcolor=$aFrameColor;
$this->bcolor=$aBorderColor;
$this->shadow=$aShadow;
}

// Hide the text
function Hide($aHide=true) {
$this->hide=$aHide;
}

// 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;
}

// 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 die("JpGraph Error: Invalid direction specified for text.");
}

// Total width of text
function GetWidth(&$aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
return $aImg->GetTextWidth($this->t);
}

// Hight of text
function GetFontHeight(&$aImg) {
$aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
return $aImg->GetFontHeight();
}

// Display text in image
function Stroke(&$aImg) {
$aImg->SetColor($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->StrokeBoxedText($this->x,$this->y,$this->t,$this->dir,$this->fcolor,$this->bcolor,$this->shadow);
}
else {
$aImg->StrokeText($this->x,$this->y,$this->t,$this->dir);
}
}
} // Class

//===================================================
// CLASS Grid
// Description: responsible for drawing grid lines in graph
//===================================================
class Grid {
var $img;
var $scale;
var $grid_color=array(196,196,196);
var $type="solid";
var $show=false, $showMinor=false,$weight=1;
//---------------
// CONSTRUCTOR
function Grid(&$aAxis) {
$this->scale = &$aAxis->scale;
$this->img = &$aAxis->img;
}
//---------------
// PUBLIC METHODS
function SetColor($aColor) {
$this->grid_color=$aColor;
}

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;
}

// Display the grid
function Stroke() {
if( $this->showMinor )
$this->DoStroke($this->scale->ticks->ticks_pos);
else
$this->DoStroke($this->scale->ticks->maj_ticks_pos);
}

//--------------
// Private methods
// Draw the grid
function DoStroke(&$aTicksPos) {
if( !$this->show )
return;
$this->img->SetColor($this->grid_color);
$this->img->SetLineWeight($this->weight);
$nbrgrids = count($aTicksPos);
if( $this->scale->type=="y" ) {
$xl=$this->img->left_margin;
$xr=$this->img->width-$this->img->right_margin;
for($i=0; $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);
}
}

if( $this->scale->type=="x" ) {
$yu=$this->img->top_margin;
$yl=$this->img->height-$this->img->bottom_margin;
$x=$aTicksPos[0];
$limit=$this->img->width-$this->img->right_margin;
$i=0;
// 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
while( $x<=$limit && $i $x=$aTicksPos[$i];
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;
}
}
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;
var $show_first_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=6;
//---------------
// 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=6;
}
else {
$this->title_margin = 5;
$this->title_adjust="high";
$this->title->SetOrientation(0);
$this->tick_label_margin=3;
}

}
//---------------
// PUBLIC METHODS

// Utility function to set the direction for tick marks
function SetTickDirection($aDir) {
$this->scale->ticks->SetDirection($aDir);
}

function SetLabelFormatString($aFormStr) {
$this->scale->ticks->SetLabelFormat($aFormStr);
}

function SetLabelFormatCallback($aFuncName) {
$this->scale->ticks->SetFormatCallback($aFuncName);
}

// Don't display the first label
function HideFirstTickLabel($aHide=false) {
$this->show_first_label=$aHide;
}

// Hide the axis
function Hide($aHide=true) {
$this->hide=$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;
}

// Specify text labels for the ticks. One label for each data point
function SetTickLabels($aLabelArray) {
$this->ticks_label = $aLabelArray;
}

// How far from the axis should the labels be drawn
function SetTickLabelMargin($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) {
die("JpGraph Error: 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 )
die("JpGraph Error: 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->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;
}

// Which side of the axis should the axis title be?
function SetTitleSide($aSideOfAxis) {
$this->title_side = $aSideOfAxis;
}

// 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=="min" ) {
$pos = $aOtherAxisScale->scale_abs[0];
}
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" ) {
$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;
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->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");
}
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.
$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;
$halign="right";
}
else {
$x += $this->title_margin;
$halign="left";
}
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");
}
$this->scale->ticks->Stroke($this->img,$this->scale,$pos);
$this->StrokeLabels($pos);
$this->title->Stroke($this->img);
}

// 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;
}

//---------------
// PRIVATE METHODS
// Draw all the tick labels on major tick marks
function StrokeLabels($aPos,$aMinor=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.)
if( $this->show_first_label ) $start=0;
else $start=1;

// 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" )
// $limit=$this->img->width-$this->img->right_margin;
//else
// $limit=$this->img->height;

// $i holds the current index for the label
$i=$start;

// Now run through all labels making sure we don't overshoot the end
// of the scale.
while( $i // $tpos holds the absolute text position for the label
$tpos=$this->scale->ticks->maj_ticklabels_pos[$i];
// we only draw every $label_step label
if( ($i % $this->label_step)==0 ) {

// 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
if( isset($this->ticks_label[$i*$m]) )
$label=$this->ticks_label[$i*$m];
else
$label=$this->scale->ticks->maj_ticks_label[$i];

if( $this->scale->type == "x" ) {
if( $this->label_angle==0 || $this->label_angle==90 )
$this->img->SetTextAlign("center","top");
else
$this->img->SetTextAlign("topanchor","top");
$this->img->StrokeText($tpos,$aPos+$this->tick_label_margin,$label,$this->label_angle);
}
else {
// scale->type == "y"
if( $this->label_angle!=0 )
die("JpGraph Error: Labels at an angle are not supported on Y-axis");
if( $this->labelPos == 0 ) { // To the left of y-axis
$this->img->SetTextAlign("right","center");
$this->img->StrokeText($aPos-$this->tick_label_margin,$tpos,$label);
}
else { // To the right of the y-axis
$this->img->SetTextAlign("left","center");
$this->img->StrokeText($aPos+$this->tick_label_margin,$tpos,$label);
}
}
}
++$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 $ticks_pos=array(); // Save abs position of minor ticks
var $maj_ticks_pos=array(); // Save abs position of major ticks
var $maj_ticklabels_pos=array(); // Save abs position of major labels
var $ticks_label=array(), $maj_ticks_label=array();
var $is_set=false;
var $precision=-1;
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;
}

//---------------
// 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;
}

// 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 automtic labels
// Deprecated from 1.4. Use SetFormatString() instead
function SetPrecision($aPrecision) {
$this->precision=$aPrecision;
}

// 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->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() {
// Empty
}

//---------------
// 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) {
if( $aMajStep <= 0 || $aMinStep <= 0 ) {
die("JpGraph Error: Minor or major step size is 0. Check that you haven't
got an accidental SetTextTicks(0) in your code.


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 )
die("JpGraph Error: 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 similair abnormality (a line needs two points!).");
$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 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;
for( $i=0; $label<=$scale->GetMaxVal()+$this->label_offset; ++$i ) {
$x=$scale->scale_abs[0]+$start_abs+$i*$maj_step_abs+$this->xlabel_offset*$min_step_abs;
$this->maj_ticklabels_pos[$i]=ceil($x);

// Apply format
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;

// 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+$i*$maj_step_abs+$this->xtick_offset*$min_step_abs;
$this->maj_ticks_pos[$i]=ceil($xtick);
if(!($this->xtick_offset > 0 && $i==$nbrmajticks-1) && !$this->supress_tickmarks) {
if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
$img->Line($xtick,$pos,$xtick,$yu);
if( $this->majcolor!="" ) $img->PopColor();
}
}
// Draw the minor tick marks

$yu = $pos - $this->direction*$this->GetMinTickAbsSize();
$label = $scale->GetMinVal();
for( $i=0,$x=$scale->scale_abs[0]; $x $x=$scale-&gt

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9812031/viewspace-926942/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/9812031/viewspace-926942/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值