jpgraph.php
上传用户:gzy2002
上传日期:2010-02-11
资源大小:1785k
文件大小:248k
源码类别:

电子政务应用

开发平台:

Java

  1. $this->autoscale_max=$aMax;
  2.     }
  3.     // If the user manually specifies a scale should the ticks
  4.     // still be set automatically?
  5.     function SetAutoTicks($aFlag=true) {
  6. $this->auto_ticks = $aFlag;
  7.     }
  8.     // Specify scale "grace" value (top and bottom)
  9.     function SetGrace($aGraceTop,$aGraceBottom=0) {
  10. if( $aGraceTop<0 || $aGraceBottom < 0  )
  11.     JpGraphError::Raise(" Grace must be larger then 0");
  12. $this->gracetop=$aGraceTop;
  13. $this->gracebottom=$aGraceBottom;
  14.     }
  15.     // Get the minimum value in the scale
  16.     function GetMinVal() {
  17. return $this->scale[0];
  18.     }
  19.     // get maximum value for scale
  20.     function GetMaxVal() {
  21. return $this->scale[1];
  22.     }
  23.     // Specify a new min/max value for sclae
  24.     function Update(&$aImg,$aMin,$aMax) {
  25. $this->scale=array($aMin,$aMax);
  26. $this->world_size=$aMax-$aMin;
  27. $this->InitConstants($aImg);
  28.     }
  29.     // Translate between world and screen
  30.     function Translate($aCoord) {
  31. return $this->off+($aCoord - $this->GetMinVal()) * $this->scale_factor; 
  32.     }
  33.     // Relative translate (don't include offset) usefull when we just want
  34.     // to know the relative position (in pixels) on the axis
  35.     function RelTranslate($aCoord) {
  36. return ($aCoord - $this->GetMinVal()) * $this->scale_factor; 
  37.     }
  38.     // Restrict autoscaling to only use integers
  39.     function SetIntScale($aIntScale=true) {
  40. $this->intscale=$aIntScale;
  41.     }
  42.     // Calculate an integer autoscale
  43.     function IntAutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
  44. // Make sure limits are integers
  45. $min=floor($min);
  46. $max=ceil($max);
  47. if( abs($min-$max)==0 ) {
  48.     --$min; ++$max;
  49. }
  50. $maxsteps = floor($maxsteps);
  51. $gracetop=round(($this->gracetop/100.0)*abs($max-$min));
  52. $gracebottom=round(($this->gracebottom/100.0)*abs($max-$min));
  53. if( is_numeric($this->autoscale_min) ) {
  54.     $min = ceil($this->autoscale_min);
  55.     if( $min >= $max ) {
  56. 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.');
  57. die();
  58.     }
  59. }
  60. if( is_numeric($this->autoscale_max) ) {
  61.     $max = ceil($this->autoscale_max);
  62.     if( $min >= $max ) {
  63. 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.');
  64. die();
  65.     }
  66. }
  67. if( abs($min-$max ) == 0 ) {
  68.     ++$max;
  69.     --$min;
  70. }
  71. $min -= $gracebottom;
  72. $max += $gracetop;
  73. // First get tickmarks as multiples of 1, 10, ...
  74. if( $majend ) {
  75.     list($num1steps,$adj1min,$adj1max,$maj1step) = 
  76. $this->IntCalcTicks($maxsteps,$min,$max,1);
  77. }
  78. else {
  79.     $adj1min = $min;
  80.     $adj1max = $max;
  81.     list($num1steps,$maj1step) = 
  82. $this->IntCalcTicksFreeze($maxsteps,$min,$max,1);
  83. }
  84. if( abs($min-$max) > 2 ) {
  85.     // Then get tick marks as 2:s 2, 20, ...
  86.     if( $majend ) {
  87. list($num2steps,$adj2min,$adj2max,$maj2step) = 
  88.     $this->IntCalcTicks($maxsteps,$min,$max,5);
  89.     }
  90.     else {
  91. $adj2min = $min;
  92. $adj2max = $max;
  93. list($num2steps,$maj2step) = 
  94.     $this->IntCalcTicksFreeze($maxsteps,$min,$max,5);
  95.     }
  96. }
  97. else {
  98.     $num2steps = 10000; // Dummy high value so we don't choose this
  99. }
  100. if( abs($min-$max) > 5 ) {
  101.     // Then get tickmarks as 5:s 5, 50, 500, ...
  102.     if( $majend ) {
  103. list($num5steps,$adj5min,$adj5max,$maj5step) = 
  104.     $this->IntCalcTicks($maxsteps,$min,$max,2);
  105.     }
  106.     else {
  107. $adj5min = $min;
  108. $adj5max = $max;
  109. list($num5steps,$maj5step) = 
  110.     $this->IntCalcTicksFreeze($maxsteps,$min,$max,2);
  111.     }
  112. }
  113. else {
  114.     $num5steps = 10000; // Dummy high value so we don't choose this
  115. }
  116. // Check to see whichof 1:s, 2:s or 5:s fit better with
  117. // the requested number of major ticks
  118. $match1=abs($num1steps-$maxsteps);
  119. $match2=abs($num2steps-$maxsteps);
  120. if( !empty($maj5step) && $maj5step > 1 )
  121.     $match5=abs($num5steps-$maxsteps);
  122. else
  123.     $match5=10000;  // Dummy high value 
  124. // Compare these three values and see which is the closest match
  125. // We use a 0.6 weight to gravitate towards multiple of 5:s 
  126. if( $match1 < $match2 ) {
  127.     if( $match1 < $match5 )
  128. $r=1;
  129.     else 
  130. $r=3;
  131. }
  132. else {
  133.     if( $match2 < $match5 )
  134. $r=2;
  135.     else 
  136. $r=3;
  137. }
  138. // Minsteps are always the same as maxsteps for integer scale
  139. switch( $r ) {
  140.     case 1:
  141. $this->Update($img,$adj1min,$adj1max);
  142. $this->ticks->Set($maj1step,$maj1step);
  143. break;
  144.     case 2:
  145. $this->Update($img,$adj2min,$adj2max);
  146. $this->ticks->Set($maj2step,$maj2step);
  147. break;
  148.     case 3:
  149. $this->Update($img,$adj5min,$adj5max);
  150. $this->ticks->Set($maj5step,$maj2step);
  151. break;
  152. }
  153.     }
  154.     // Calculate autoscale. Used if user hasn't given a scale and ticks
  155.     // $maxsteps is the maximum number of major tickmarks allowed.
  156.     function AutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
  157. if( $this->intscale ) {
  158.     $this->IntAutoScale($img,$min,$max,$maxsteps,$majend);
  159.     return;
  160. }
  161. if( abs($min-$max) < 0.00001 ) {
  162.     // We need some difference to be able to autoscale
  163.     // make it 5% above and 5% below value
  164.     if( $min==0 && $max==0 ) { // Special case
  165. $min=-1; $max=1;
  166.     }
  167.     else {
  168. $delta = (abs($max)+abs($min))*0.005;
  169. $min -= $delta;
  170. $max += $delta;
  171.     }
  172. }
  173. $gracetop=($this->gracetop/100.0)*abs($max-$min);
  174. $gracebottom=($this->gracebottom/100.0)*abs($max-$min);
  175. if( is_numeric($this->autoscale_min) ) {
  176.     $min = $this->autoscale_min;
  177.     if( $min >= $max ) {
  178. 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.');
  179. die();
  180.     }
  181.     if( abs($min-$max ) < 0.00001 )
  182. $max *= 1.2;
  183. }
  184. if( is_numeric($this->autoscale_max) ) {
  185.     $max = $this->autoscale_max;
  186.     if( $min >= $max ) {
  187. 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.');
  188. die();
  189.     }
  190.     if( abs($min-$max ) < 0.00001 )
  191. $min *= 0.8;
  192. }
  193. $min -= $gracebottom;
  194. $max += $gracetop;
  195. // First get tickmarks as multiples of 0.1, 1, 10, ...
  196. if( $majend ) {
  197.     list($num1steps,$adj1min,$adj1max,$min1step,$maj1step) = 
  198. $this->CalcTicks($maxsteps,$min,$max,1,2);
  199. }
  200. else {
  201.     $adj1min=$min;
  202.     $adj1max=$max;
  203.     list($num1steps,$min1step,$maj1step) = 
  204. $this->CalcTicksFreeze($maxsteps,$min,$max,1,2,false);
  205. }
  206. // Then get tick marks as 2:s 0.2, 2, 20, ...
  207. if( $majend ) {
  208.     list($num2steps,$adj2min,$adj2max,$min2step,$maj2step) = 
  209. $this->CalcTicks($maxsteps,$min,$max,5,2);
  210. }
  211. else {
  212.     $adj2min=$min;
  213.     $adj2max=$max;
  214.     list($num2steps,$min2step,$maj2step) = 
  215. $this->CalcTicksFreeze($maxsteps,$min,$max,5,2,false);
  216. }
  217. // Then get tickmarks as 5:s 0.05, 0.5, 5, 50, ...
  218. if( $majend ) {
  219.     list($num5steps,$adj5min,$adj5max,$min5step,$maj5step) = 
  220. $this->CalcTicks($maxsteps,$min,$max,2,5);
  221. }
  222. else {
  223.     $adj5min=$min;
  224.     $adj5max=$max;
  225.     list($num5steps,$min5step,$maj5step) = 
  226. $this->CalcTicksFreeze($maxsteps,$min,$max,2,5,false);
  227. }
  228. // Check to see whichof 1:s, 2:s or 5:s fit better with
  229. // the requested number of major ticks
  230. $match1=abs($num1steps-$maxsteps);
  231. $match2=abs($num2steps-$maxsteps);
  232. $match5=abs($num5steps-$maxsteps);
  233. // Compare these three values and see which is the closest match
  234. // We use a 0.8 weight to gravitate towards multiple of 5:s 
  235. $r=$this->MatchMin3($match1,$match2,$match5,0.8);
  236. switch( $r ) {
  237.     case 1:
  238. $this->Update($img,$adj1min,$adj1max);
  239. $this->ticks->Set($maj1step,$min1step);
  240. break;
  241.     case 2:
  242. $this->Update($img,$adj2min,$adj2max);
  243. $this->ticks->Set($maj2step,$min2step);
  244. break;
  245.     case 3:
  246. $this->Update($img,$adj5min,$adj5max);
  247. $this->ticks->Set($maj5step,$min5step);
  248. break;
  249. }
  250.     }
  251. //---------------
  252. // PRIVATE METHODS
  253.     // This method recalculates all constants that are depending on the
  254.     // margins in the image. If the margins in the image are changed
  255.     // this method should be called for every scale that is registred with
  256.     // that image. Should really be installed as an observer of that image.
  257.     function InitConstants(&$img) {
  258. if( $this->type=="x" ) {
  259.     $this->world_abs_size=$img->width - $img->left_margin - $img->right_margin;
  260.     $this->off=$img->left_margin;
  261.     $this->scale_factor = 0;
  262.     if( $this->world_size > 0 )
  263. $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
  264. }
  265. else { // y scale
  266.     $this->world_abs_size=$img->height - $img->top_margin - $img->bottom_margin; 
  267.     $this->off=$img->top_margin+$this->world_abs_size;
  268.     $this->scale_factor = 0;
  269.     if( $this->world_size > 0 )
  270. $this->scale_factor=-$this->world_abs_size/($this->world_size*1.0);
  271. }
  272. $size = $this->world_size * $this->scale_factor;
  273. $this->scale_abs=array($this->off,$this->off + $size);
  274.     }
  275.     // Initialize the conversion constants for this scale
  276.     // This tries to pre-calculate as much as possible to speed up the
  277.     // actual conversion (with Translate()) later on
  278.     // $start =scale start in absolute pixels (for x-scale this is an y-position
  279.     //  and for an y-scale this is an x-position
  280.     // $len  =absolute length in pixels of scale 
  281.     function SetConstants($aStart,$aLen) {
  282. $this->world_abs_size=$aLen;
  283. $this->off=$aStart;
  284. if( $this->world_size<=0 ) {
  285.     JpGraphError::Raise("<b>JpGraph Fatal Error</b>:<br>
  286.  You have unfortunately stumbled upon a bug in JpGraph. <br>
  287.  It seems like the scale range is ".$this->world_size." [for ".
  288. $this->type." scale] <br>
  289.          Please report Bug #01 to jpgraph@aditus.nu and include the script
  290.  that gave this error. <br>
  291.  This problem could potentially be caused by trying to use "illegal"
  292.  values in the input data arrays (like trying to send in strings or
  293.  only NULL values) which causes the autoscaling to fail.");
  294. }
  295. // scale_factor = number of pixels per world unit
  296. $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
  297. // scale_abs = start and end points of scale in absolute pixels
  298. $this->scale_abs=array($this->off,$this->off+$this->world_size*$this->scale_factor);
  299.     }
  300.     // Calculate number of ticks steps with a specific division
  301.     // $a is the divisor of 10**x to generate the first maj tick intervall
  302.     // $a=1, $b=2 give major ticks with multiple of 10, ...,0.1,1,10,...
  303.     // $a=5, $b=2 give major ticks with multiple of 2:s ...,0.2,2,20,...
  304.     // $a=2, $b=5 give major ticks with multiple of 5:s ...,0.5,5,50,...
  305.     // We return a vector of
  306.     //  [$numsteps,$adjmin,$adjmax,$minstep,$majstep]
  307.     // If $majend==true then the first and last marks on the axis will be major
  308.     // labeled tick marks otherwise it will be adjusted to the closest min tick mark
  309.     function CalcTicks($maxsteps,$min,$max,$a,$b,$majend=true) {
  310. $diff=$max-$min; 
  311. if( $diff==0 )
  312.     $ld=0;
  313. else
  314.     $ld=floor(log10($diff));
  315. // Gravitate min towards zero if we are close
  316. if( $min>0 && $min < pow(10,$ld) ) $min=0;
  317. //$majstep=pow(10,$ld-1)/$a; 
  318. $majstep=pow(10,$ld)/$a; 
  319. $minstep=$majstep/$b;
  320. $adjmax=ceil($max/$minstep)*$minstep;
  321. $adjmin=floor($min/$minstep)*$minstep;
  322. $adjdiff = $adjmax-$adjmin;
  323. $numsteps=$adjdiff/$majstep; 
  324. while( $numsteps>$maxsteps ) {
  325.     $majstep=pow(10,$ld)/$a; 
  326.     $numsteps=$adjdiff/$majstep;
  327.     ++$ld;
  328. }
  329. $minstep=$majstep/$b;
  330. $adjmin=floor($min/$minstep)*$minstep;
  331. $adjdiff = $adjmax-$adjmin;
  332. if( $majend ) {
  333.     $adjmin = floor($min/$majstep)*$majstep;
  334.     $adjdiff = $adjmax-$adjmin;
  335.     $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
  336. }
  337. else
  338.     $adjmax=ceil($max/$minstep)*$minstep;
  339. return array($numsteps,$adjmin,$adjmax,$minstep,$majstep);
  340.     }
  341.     function CalcTicksFreeze($maxsteps,$min,$max,$a,$b) {
  342. // Same as CalcTicks but don't adjust min/max values
  343. $diff=$max-$min; 
  344. if( $diff==0 )
  345.     $ld=0;
  346. else
  347.     $ld=floor(log10($diff));
  348. //$majstep=pow(10,$ld-1)/$a; 
  349. $majstep=pow(10,$ld)/$a; 
  350. $minstep=$majstep/$b;
  351. $numsteps=floor($diff/$majstep); 
  352. while( $numsteps > $maxsteps ) {
  353.     $majstep=pow(10,$ld)/$a; 
  354.     $numsteps=floor($diff/$majstep);
  355.     ++$ld;
  356. }
  357. $minstep=$majstep/$b;
  358. return array($numsteps,$minstep,$majstep);
  359.     }
  360.     function IntCalcTicks($maxsteps,$min,$max,$a,$majend=true) {
  361. $diff=$max-$min; 
  362. if( $diff==0 )
  363.     JpGraphError::Raise('Can't automatically determine ticks since min==max.');
  364. else
  365.     $ld=floor(log10($diff));
  366. // Gravitate min towards zero if we are close
  367. if( $min>0 && $min < pow(10,$ld) ) $min=0;
  368. if( $ld == 0 ) $ld=1;
  369. if( $a == 1 ) 
  370.     $majstep = 1;
  371. else
  372.     $majstep=pow(10,$ld)/$a; 
  373. $adjmax=ceil($max/$majstep)*$majstep;
  374. $adjmin=floor($min/$majstep)*$majstep;
  375. $adjdiff = $adjmax-$adjmin;
  376. $numsteps=$adjdiff/$majstep; 
  377. while( $numsteps>$maxsteps ) {
  378.     $majstep=pow(10,$ld)/$a; 
  379.     $numsteps=$adjdiff/$majstep;
  380.     ++$ld;
  381. }
  382. $adjmin=floor($min/$majstep)*$majstep;
  383. $adjdiff = $adjmax-$adjmin;
  384. if( $majend ) {
  385.     $adjmin = floor($min/$majstep)*$majstep;
  386.     $adjdiff = $adjmax-$adjmin;
  387.     $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
  388. }
  389. else
  390.     $adjmax=ceil($max/$majstep)*$majstep;
  391. return array($numsteps,$adjmin,$adjmax,$majstep);
  392.     }
  393.     function IntCalcTicksFreeze($maxsteps,$min,$max,$a) {
  394. // Same as IntCalcTick but don't change min/max values
  395. $diff=$max-$min; 
  396. if( $diff==0 )
  397.     JpGraphError::Raise('Can't automatically determine ticks since min==max.');
  398. else
  399.     $ld=floor(log10($diff));
  400. if( $ld == 0 ) $ld=1;
  401. if( $a == 1 ) 
  402.     $majstep = 1;
  403. else
  404.     $majstep=pow(10,$ld)/$a; 
  405. $numsteps=floor($diff/$majstep); 
  406. while( $numsteps > $maxsteps ) {
  407.     $majstep=pow(10,$ld)/$a; 
  408.     $numsteps=floor($diff/$majstep);
  409.     ++$ld;
  410. }
  411. return array($numsteps,$majstep);
  412.     }
  413.     // Determine the minimum of three values witha  weight for last value
  414.     function MatchMin3($a,$b,$c,$weight) {
  415. if( $a < $b ) {
  416.     if( $a < ($c*$weight) ) 
  417. return 1; // $a smallest
  418.     else 
  419. return 3; // $c smallest
  420. }
  421. elseif( $b < ($c*$weight) ) 
  422.     return 2; // $b smallest
  423. return 3; // $c smallest
  424.     }
  425. } // Class
  426. //===================================================
  427. // CLASS RGB
  428. // Description: Color definitions as RGB triples
  429. //===================================================
  430. class RGB {
  431.     var $rgb_table;
  432.     var $img;
  433.     function RGB($aImg=null) {
  434. $this->img = $aImg;
  435. // Conversion array between color names and RGB
  436. $this->rgb_table = array(
  437.     "aqua"=> array(0,255,255),
  438.     "lime"=> array(0,255,0),
  439.     "teal"=> array(0,128,128),
  440.     "whitesmoke"=>array(245,245,245),
  441.     "gainsboro"=>array(220,220,220),
  442.     "oldlace"=>array(253,245,230),
  443.     "linen"=>array(250,240,230),
  444.     "antiquewhite"=>array(250,235,215),
  445.     "papayawhip"=>array(255,239,213),
  446.     "blanchedalmond"=>array(255,235,205),
  447.     "bisque"=>array(255,228,196),
  448.     "peachpuff"=>array(255,218,185),
  449.     "navajowhite"=>array(255,222,173),
  450.     "moccasin"=>array(255,228,181),
  451.     "cornsilk"=>array(255,248,220),
  452.     "ivory"=>array(255,255,240),
  453.     "lemonchiffon"=>array(255,250,205),
  454.     "seashell"=>array(255,245,238),
  455.     "mintcream"=>array(245,255,250),
  456.     "azure"=>array(240,255,255),
  457.     "aliceblue"=>array(240,248,255),
  458.     "lavender"=>array(230,230,250),
  459.     "lavenderblush"=>array(255,240,245),
  460.     "mistyrose"=>array(255,228,225),
  461.     "white"=>array(255,255,255),
  462.     "black"=>array(0,0,0),
  463.     "darkslategray"=>array(47,79,79),
  464.     "dimgray"=>array(105,105,105),
  465.     "slategray"=>array(112,128,144),
  466.     "lightslategray"=>array(119,136,153),
  467.     "gray"=>array(190,190,190),
  468.     "lightgray"=>array(211,211,211),
  469.     "midnightblue"=>array(25,25,112),
  470.     "navy"=>array(0,0,128),
  471.     "cornflowerblue"=>array(100,149,237),
  472.     "darkslateblue"=>array(72,61,139),
  473.     "slateblue"=>array(106,90,205),
  474.     "mediumslateblue"=>array(123,104,238),
  475.     "lightslateblue"=>array(132,112,255),
  476.     "mediumblue"=>array(0,0,205),
  477.     "royalblue"=>array(65,105,225),
  478.     "blue"=>array(0,0,255),
  479.     "dodgerblue"=>array(30,144,255),
  480.     "deepskyblue"=>array(0,191,255),
  481.     "skyblue"=>array(135,206,235),
  482.     "lightskyblue"=>array(135,206,250),
  483.     "steelblue"=>array(70,130,180),
  484.     "lightred"=>array(211,167,168),
  485.     "lightsteelblue"=>array(176,196,222),
  486.     "lightblue"=>array(173,216,230),
  487.     "powderblue"=>array(176,224,230),
  488.     "paleturquoise"=>array(175,238,238),
  489.     "darkturquoise"=>array(0,206,209),
  490.     "mediumturquoise"=>array(72,209,204),
  491.     "turquoise"=>array(64,224,208),
  492.     "cyan"=>array(0,255,255),
  493.     "lightcyan"=>array(224,255,255),
  494.     "cadetblue"=>array(95,158,160),
  495.     "mediumaquamarine"=>array(102,205,170),
  496.     "aquamarine"=>array(127,255,212),
  497.     "darkgreen"=>array(0,100,0),
  498.     "darkolivegreen"=>array(85,107,47),
  499.     "darkseagreen"=>array(143,188,143),
  500.     "seagreen"=>array(46,139,87),
  501.     "mediumseagreen"=>array(60,179,113),
  502.     "lightseagreen"=>array(32,178,170),
  503.     "palegreen"=>array(152,251,152),
  504.     "springgreen"=>array(0,255,127),
  505.     "lawngreen"=>array(124,252,0),
  506.     "green"=>array(0,255,0),
  507.     "chartreuse"=>array(127,255,0),
  508.     "mediumspringgreen"=>array(0,250,154),
  509.     "greenyellow"=>array(173,255,47),
  510.     "limegreen"=>array(50,205,50),
  511.     "yellowgreen"=>array(154,205,50),
  512.     "forestgreen"=>array(34,139,34),
  513.     "olivedrab"=>array(107,142,35),
  514.     "darkkhaki"=>array(189,183,107),
  515.     "khaki"=>array(240,230,140),
  516.     "palegoldenrod"=>array(238,232,170),
  517.     "lightgoldenrodyellow"=>array(250,250,210),
  518.     "lightyellow"=>array(255,255,200),
  519.     "yellow"=>array(255,255,0),
  520.     "gold"=>array(255,215,0),
  521.     "lightgoldenrod"=>array(238,221,130),
  522.     "goldenrod"=>array(218,165,32),
  523.     "darkgoldenrod"=>array(184,134,11),
  524.     "rosybrown"=>array(188,143,143),
  525.     "indianred"=>array(205,92,92),
  526.     "saddlebrown"=>array(139,69,19),
  527.     "sienna"=>array(160,82,45),
  528.     "peru"=>array(205,133,63),
  529.     "burlywood"=>array(222,184,135),
  530.     "beige"=>array(245,245,220),
  531.     "wheat"=>array(245,222,179),
  532.     "sandybrown"=>array(244,164,96),
  533.     "tan"=>array(210,180,140),
  534.     "chocolate"=>array(210,105,30),
  535.     "firebrick"=>array(178,34,34),
  536.     "brown"=>array(165,42,42),
  537.     "darksalmon"=>array(233,150,122),
  538.     "salmon"=>array(250,128,114),
  539.     "lightsalmon"=>array(255,160,122),
  540.     "orange"=>array(255,165,0),
  541.     "darkorange"=>array(255,140,0),
  542.     "coral"=>array(255,127,80),
  543.     "lightcoral"=>array(240,128,128),
  544.     "tomato"=>array(255,99,71),
  545.     "orangered"=>array(255,69,0),
  546.     "red"=>array(255,0,0),
  547.     "hotpink"=>array(255,105,180),
  548.     "deeppink"=>array(255,20,147),
  549.     "pink"=>array(255,192,203),
  550.     "lightpink"=>array(255,182,193),
  551.     "palevioletred"=>array(219,112,147),
  552.     "maroon"=>array(176,48,96),
  553.     "mediumvioletred"=>array(199,21,133),
  554.     "violetred"=>array(208,32,144),
  555.     "magenta"=>array(255,0,255),
  556.     "violet"=>array(238,130,238),
  557.     "plum"=>array(221,160,221),
  558.     "orchid"=>array(218,112,214),
  559.     "mediumorchid"=>array(186,85,211),
  560.     "darkorchid"=>array(153,50,204),
  561.     "darkviolet"=>array(148,0,211),
  562.     "blueviolet"=>array(138,43,226),
  563.     "purple"=>array(160,32,240),
  564.     "mediumpurple"=>array(147,112,219),
  565.     "thistle"=>array(216,191,216),
  566.     "snow1"=>array(255,250,250),
  567.     "snow2"=>array(238,233,233),
  568.     "snow3"=>array(205,201,201),
  569.     "snow4"=>array(139,137,137),
  570.     "seashell1"=>array(255,245,238),
  571.     "seashell2"=>array(238,229,222),
  572.     "seashell3"=>array(205,197,191),
  573.     "seashell4"=>array(139,134,130),
  574.     "AntiqueWhite1"=>array(255,239,219),
  575.     "AntiqueWhite2"=>array(238,223,204),
  576.     "AntiqueWhite3"=>array(205,192,176),
  577.     "AntiqueWhite4"=>array(139,131,120),
  578.     "bisque1"=>array(255,228,196),
  579.     "bisque2"=>array(238,213,183),
  580.     "bisque3"=>array(205,183,158),
  581.     "bisque4"=>array(139,125,107),
  582.     "peachPuff1"=>array(255,218,185),
  583.     "peachpuff2"=>array(238,203,173),
  584.     "peachpuff3"=>array(205,175,149),
  585.     "peachpuff4"=>array(139,119,101),
  586.     "navajowhite1"=>array(255,222,173),
  587.     "navajowhite2"=>array(238,207,161),
  588.     "navajowhite3"=>array(205,179,139),
  589.     "navajowhite4"=>array(139,121,94),
  590.     "lemonchiffon1"=>array(255,250,205),
  591.     "lemonchiffon2"=>array(238,233,191),
  592.     "lemonchiffon3"=>array(205,201,165),
  593.     "lemonchiffon4"=>array(139,137,112),
  594.     "ivory1"=>array(255,255,240),
  595.     "ivory2"=>array(238,238,224),
  596.     "ivory3"=>array(205,205,193),
  597.     "ivory4"=>array(139,139,131),
  598.     "honeydew"=>array(193,205,193),
  599.     "lavenderblush1"=>array(255,240,245),
  600.     "lavenderblush2"=>array(238,224,229),
  601.     "lavenderblush3"=>array(205,193,197),
  602.     "lavenderblush4"=>array(139,131,134),
  603.     "mistyrose1"=>array(255,228,225),
  604.     "mistyrose2"=>array(238,213,210),
  605.     "mistyrose3"=>array(205,183,181),
  606.     "mistyrose4"=>array(139,125,123),
  607.     "azure1"=>array(240,255,255),
  608.     "azure2"=>array(224,238,238),
  609.     "azure3"=>array(193,205,205),
  610.     "azure4"=>array(131,139,139),
  611.     "slateblue1"=>array(131,111,255),
  612.     "slateblue2"=>array(122,103,238),
  613.     "slateblue3"=>array(105,89,205),
  614.     "slateblue4"=>array(71,60,139),
  615.     "royalblue1"=>array(72,118,255),
  616.     "royalblue2"=>array(67,110,238),
  617.     "royalblue3"=>array(58,95,205),
  618.     "royalblue4"=>array(39,64,139),
  619.     "dodgerblue1"=>array(30,144,255),
  620.     "dodgerblue2"=>array(28,134,238),
  621.     "dodgerblue3"=>array(24,116,205),
  622.     "dodgerblue4"=>array(16,78,139),
  623.     "steelblue1"=>array(99,184,255),
  624.     "steelblue2"=>array(92,172,238),
  625.     "steelblue3"=>array(79,148,205),
  626.     "steelblue4"=>array(54,100,139),
  627.     "deepskyblue1"=>array(0,191,255),
  628.     "deepskyblue2"=>array(0,178,238),
  629.     "deepskyblue3"=>array(0,154,205),
  630.     "deepskyblue4"=>array(0,104,139),
  631.     "skyblue1"=>array(135,206,255),
  632.     "skyblue2"=>array(126,192,238),
  633.     "skyblue3"=>array(108,166,205),
  634.     "skyblue4"=>array(74,112,139),
  635.     "lightskyblue1"=>array(176,226,255),
  636.     "lightskyblue2"=>array(164,211,238),
  637.     "lightskyblue3"=>array(141,182,205),
  638.     "lightskyblue4"=>array(96,123,139),
  639.     "slategray1"=>array(198,226,255),
  640.     "slategray2"=>array(185,211,238),
  641.     "slategray3"=>array(159,182,205),
  642.     "slategray4"=>array(108,123,139),
  643.     "lightsteelblue1"=>array(202,225,255),
  644.     "lightsteelblue2"=>array(188,210,238),
  645.     "lightsteelblue3"=>array(162,181,205),
  646.     "lightsteelblue4"=>array(110,123,139),
  647.     "lightblue1"=>array(191,239,255),
  648.     "lightblue2"=>array(178,223,238),
  649.     "lightblue3"=>array(154,192,205),
  650.     "lightblue4"=>array(104,131,139),
  651.     "lightcyan1"=>array(224,255,255),
  652.     "lightcyan2"=>array(209,238,238),
  653.     "lightcyan3"=>array(180,205,205),
  654.     "lightcyan4"=>array(122,139,139),
  655.     "paleturquoise1"=>array(187,255,255),
  656.     "paleturquoise2"=>array(174,238,238),
  657.     "paleturquoise3"=>array(150,205,205),
  658.     "paleturquoise4"=>array(102,139,139),
  659.     "cadetblue1"=>array(152,245,255),
  660.     "cadetblue2"=>array(142,229,238),
  661.     "cadetblue3"=>array(122,197,205),
  662.     "cadetblue4"=>array(83,134,139),
  663.     "turquoise1"=>array(0,245,255),
  664.     "turquoise2"=>array(0,229,238),
  665.     "turquoise3"=>array(0,197,205),
  666.     "turquoise4"=>array(0,134,139),
  667.     "cyan1"=>array(0,255,255),
  668.     "cyan2"=>array(0,238,238),
  669.     "cyan3"=>array(0,205,205),
  670.     "cyan4"=>array(0,139,139),
  671.     "darkslategray1"=>array(151,255,255),
  672.     "darkslategray2"=>array(141,238,238),
  673.     "darkslategray3"=>array(121,205,205),
  674.     "darkslategray4"=>array(82,139,139),
  675.     "aquamarine1"=>array(127,255,212),
  676.     "aquamarine2"=>array(118,238,198),
  677.     "aquamarine3"=>array(102,205,170),
  678.     "aquamarine4"=>array(69,139,116),
  679.     "darkseagreen1"=>array(193,255,193),
  680.     "darkseagreen2"=>array(180,238,180),
  681.     "darkseagreen3"=>array(155,205,155),
  682.     "darkseagreen4"=>array(105,139,105),
  683.     "seagreen1"=>array(84,255,159),
  684.     "seagreen2"=>array(78,238,148),
  685.     "seagreen3"=>array(67,205,128),
  686.     "seagreen4"=>array(46,139,87),
  687.     "palegreen1"=>array(154,255,154),
  688.     "palegreen2"=>array(144,238,144),
  689.     "palegreen3"=>array(124,205,124),
  690.     "palegreen4"=>array(84,139,84),
  691.     "springgreen1"=>array(0,255,127),
  692.     "springgreen2"=>array(0,238,118),
  693.     "springgreen3"=>array(0,205,102),
  694.     "springgreen4"=>array(0,139,69),
  695.     "chartreuse1"=>array(127,255,0),
  696.     "chartreuse2"=>array(118,238,0),
  697.     "chartreuse3"=>array(102,205,0),
  698.     "chartreuse4"=>array(69,139,0),
  699.     "olivedrab1"=>array(192,255,62),
  700.     "olivedrab2"=>array(179,238,58),
  701.     "olivedrab3"=>array(154,205,50),
  702.     "olivedrab4"=>array(105,139,34),
  703.     "darkolivegreen1"=>array(202,255,112),
  704.     "darkolivegreen2"=>array(188,238,104),
  705.     "darkolivegreen3"=>array(162,205,90),
  706.     "darkolivegreen4"=>array(110,139,61),
  707.     "khaki1"=>array(255,246,143),
  708.     "khaki2"=>array(238,230,133),
  709.     "khaki3"=>array(205,198,115),
  710.     "khaki4"=>array(139,134,78),
  711.     "lightgoldenrod1"=>array(255,236,139),
  712.     "lightgoldenrod2"=>array(238,220,130),
  713.     "lightgoldenrod3"=>array(205,190,112),
  714.     "lightgoldenrod4"=>array(139,129,76),
  715.     "yellow1"=>array(255,255,0),
  716.     "yellow2"=>array(238,238,0),
  717.     "yellow3"=>array(205,205,0),
  718.     "yellow4"=>array(139,139,0),
  719.     "gold1"=>array(255,215,0),
  720.     "gold2"=>array(238,201,0),
  721.     "gold3"=>array(205,173,0),
  722.     "gold4"=>array(139,117,0),
  723.     "goldenrod1"=>array(255,193,37),
  724.     "goldenrod2"=>array(238,180,34),
  725.     "goldenrod3"=>array(205,155,29),
  726.     "goldenrod4"=>array(139,105,20),
  727.     "darkgoldenrod1"=>array(255,185,15),
  728.     "darkgoldenrod2"=>array(238,173,14),
  729.     "darkgoldenrod3"=>array(205,149,12),
  730.     "darkgoldenrod4"=>array(139,101,8),
  731.     "rosybrown1"=>array(255,193,193),
  732.     "rosybrown2"=>array(238,180,180),
  733.     "rosybrown3"=>array(205,155,155),
  734.     "rosybrown4"=>array(139,105,105),
  735.     "indianred1"=>array(255,106,106),
  736.     "indianred2"=>array(238,99,99),
  737.     "indianred3"=>array(205,85,85),
  738.     "indianred4"=>array(139,58,58),
  739.     "sienna1"=>array(255,130,71),
  740.     "sienna2"=>array(238,121,66),
  741.     "sienna3"=>array(205,104,57),
  742.     "sienna4"=>array(139,71,38),
  743.     "burlywood1"=>array(255,211,155),
  744.     "burlywood2"=>array(238,197,145),
  745.     "burlywood3"=>array(205,170,125),
  746.     "burlywood4"=>array(139,115,85),
  747.     "wheat1"=>array(255,231,186),
  748.     "wheat2"=>array(238,216,174),
  749.     "wheat3"=>array(205,186,150),
  750.     "wheat4"=>array(139,126,102),
  751.     "tan1"=>array(255,165,79),
  752.     "tan2"=>array(238,154,73),
  753.     "tan3"=>array(205,133,63),
  754.     "tan4"=>array(139,90,43),
  755.     "chocolate1"=>array(255,127,36),
  756.     "chocolate2"=>array(238,118,33),
  757.     "chocolate3"=>array(205,102,29),
  758.     "chocolate4"=>array(139,69,19),
  759.     "firebrick1"=>array(255,48,48),
  760.     "firebrick2"=>array(238,44,44),
  761.     "firebrick3"=>array(205,38,38),
  762.     "firebrick4"=>array(139,26,26),
  763.     "brown1"=>array(255,64,64),
  764.     "brown2"=>array(238,59,59),
  765.     "brown3"=>array(205,51,51),
  766.     "brown4"=>array(139,35,35),
  767.     "salmon1"=>array(255,140,105),
  768.     "salmon2"=>array(238,130,98),
  769.     "salmon3"=>array(205,112,84),
  770.     "salmon4"=>array(139,76,57),
  771.     "lightsalmon1"=>array(255,160,122),
  772.     "lightsalmon2"=>array(238,149,114),
  773.     "lightsalmon3"=>array(205,129,98),
  774.     "lightsalmon4"=>array(139,87,66),
  775.     "orange1"=>array(255,165,0),
  776.     "orange2"=>array(238,154,0),
  777.     "orange3"=>array(205,133,0),
  778.     "orange4"=>array(139,90,0),
  779.     "darkorange1"=>array(255,127,0),
  780.     "darkorange2"=>array(238,118,0),
  781.     "darkorange3"=>array(205,102,0),
  782.     "darkorange4"=>array(139,69,0),
  783.     "coral1"=>array(255,114,86),
  784.     "coral2"=>array(238,106,80),
  785.     "coral3"=>array(205,91,69),
  786.     "coral4"=>array(139,62,47),
  787.     "tomato1"=>array(255,99,71),
  788.     "tomato2"=>array(238,92,66),
  789.     "tomato3"=>array(205,79,57),
  790.     "tomato4"=>array(139,54,38),
  791.     "orangered1"=>array(255,69,0),
  792.     "orangered2"=>array(238,64,0),
  793.     "orangered3"=>array(205,55,0),
  794.     "orangered4"=>array(139,37,0),
  795.     "deeppink1"=>array(255,20,147),
  796.     "deeppink2"=>array(238,18,137),
  797.     "deeppink3"=>array(205,16,118),
  798.     "deeppink4"=>array(139,10,80),
  799.     "hotpink1"=>array(255,110,180),
  800.     "hotpink2"=>array(238,106,167),
  801.     "hotpink3"=>array(205,96,144),
  802.     "hotpink4"=>array(139,58,98),
  803.     "pink1"=>array(255,181,197),
  804.     "pink2"=>array(238,169,184),
  805.     "pink3"=>array(205,145,158),
  806.     "pink4"=>array(139,99,108),
  807.     "lightpink1"=>array(255,174,185),
  808.     "lightpink2"=>array(238,162,173),
  809.     "lightpink3"=>array(205,140,149),
  810.     "lightpink4"=>array(139,95,101),
  811.     "palevioletred1"=>array(255,130,171),
  812.     "palevioletred2"=>array(238,121,159),
  813.     "palevioletred3"=>array(205,104,137),
  814.     "palevioletred4"=>array(139,71,93),
  815.     "maroon1"=>array(255,52,179),
  816.     "maroon2"=>array(238,48,167),
  817.     "maroon3"=>array(205,41,144),
  818.     "maroon4"=>array(139,28,98),
  819.     "violetred1"=>array(255,62,150),
  820.     "violetred2"=>array(238,58,140),
  821.     "violetred3"=>array(205,50,120),
  822.     "violetred4"=>array(139,34,82),
  823.     "magenta1"=>array(255,0,255),
  824.     "magenta2"=>array(238,0,238),
  825.     "magenta3"=>array(205,0,205),
  826.     "magenta4"=>array(139,0,139),
  827.     "mediumred"=>array(140,34,34),         
  828.     "orchid1"=>array(255,131,250),
  829.     "orchid2"=>array(238,122,233),
  830.     "orchid3"=>array(205,105,201),
  831.     "orchid4"=>array(139,71,137),
  832.     "plum1"=>array(255,187,255),
  833.     "plum2"=>array(238,174,238),
  834.     "plum3"=>array(205,150,205),
  835.     "plum4"=>array(139,102,139),
  836.     "mediumorchid1"=>array(224,102,255),
  837.     "mediumorchid2"=>array(209,95,238),
  838.     "mediumorchid3"=>array(180,82,205),
  839.     "mediumorchid4"=>array(122,55,139),
  840.     "darkorchid1"=>array(191,62,255),
  841.     "darkorchid2"=>array(178,58,238),
  842.     "darkorchid3"=>array(154,50,205),
  843.     "darkorchid4"=>array(104,34,139),
  844.     "purple1"=>array(155,48,255),
  845.     "purple2"=>array(145,44,238),
  846.     "purple3"=>array(125,38,205),
  847.     "purple4"=>array(85,26,139),
  848.     "mediumpurple1"=>array(171,130,255),
  849.     "mediumpurple2"=>array(159,121,238),
  850.     "mediumpurple3"=>array(137,104,205),
  851.     "mediumpurple4"=>array(93,71,139),
  852.     "thistle1"=>array(255,225,255),
  853.     "thistle2"=>array(238,210,238),
  854.     "thistle3"=>array(205,181,205),
  855.     "thistle4"=>array(139,123,139),
  856.     "gray1"=>array(10,10,10),
  857.     "gray2"=>array(40,40,30),
  858.     "gray3"=>array(70,70,70),
  859.     "gray4"=>array(100,100,100),
  860.     "gray5"=>array(130,130,130),
  861.     "gray6"=>array(160,160,160),
  862.     "gray7"=>array(190,190,190),
  863.     "gray8"=>array(210,210,210),
  864.     "gray9"=>array(240,240,240),
  865.     "darkgray"=>array(100,100,100),
  866.     "darkblue"=>array(0,0,139),
  867.     "darkcyan"=>array(0,139,139),
  868.     "darkmagenta"=>array(139,0,139),
  869.     "darkred"=>array(139,0,0),
  870.     "silver"=>array(192, 192, 192),
  871.     "eggplant"=>array(144,176,168),
  872.     "lightgreen"=>array(144,238,144));
  873.     }
  874. //----------------
  875. // PUBLIC METHODS
  876.     // Colors can be specified as either
  877.     // 1. #xxxxxx HTML style
  878.     // 2. "colorname"  as a named color
  879.     // 3. array(r,g,b) RGB triple
  880.     // This function translates this to a native RGB format and returns an 
  881.     // RGB triple.
  882.     function Color($aColor) {
  883. if (is_string($aColor)) {
  884.     // Strip of any alpha factor
  885.     $pos = strpos($aColor,'@');
  886.     if( $pos === false ) {
  887. $alpha = 0;
  888.     }
  889.     else {
  890. $pos2 = strpos($aColor,':');
  891. if( $pos2===false ) 
  892.     $pos2 = $pos-1; // Sentinel
  893. if( $pos > $pos2 ) {
  894.     $alpha = substr($aColor,$pos+1);
  895.     $aColor = substr($aColor,0,$pos);
  896. }
  897. else {
  898.     $alpha = substr($aColor,$pos+1,$pos2-$pos-1);
  899.     $aColor = substr($aColor,0,$pos).substr($aColor,$pos2);
  900. }
  901.     }
  902.     // Extract potential adjustment figure at end of color
  903.     // specification
  904.     $pos = strpos($aColor,":");
  905.     if( $pos === false ) {
  906. $adj = 1.0;
  907.     }
  908.     else {
  909. $adj = 0.0 + substr($aColor,$pos+1);
  910. $aColor = substr($aColor,0,$pos);
  911.     }
  912.     if( $adj < 0 )
  913. JpGraphError::Raise('Adjustment factor for color must be > 0');
  914.     if (substr($aColor, 0, 1) == "#") {
  915. $r = hexdec(substr($aColor, 1, 2));
  916. $g = hexdec(substr($aColor, 3, 2));
  917. $b = hexdec(substr($aColor, 5, 2));
  918.     } else {
  919.        if(!isset($this->rgb_table[$aColor]) )
  920.     JpGraphError::Raise(" Unknown color: <strong>$aColor</strong>");
  921. $tmp=$this->rgb_table[$aColor];
  922. $r = $tmp[0];
  923. $g = $tmp[1];
  924. $b = $tmp[2];
  925.     }
  926.     // Scale adj so that an adj=2 always
  927.     // makes the color 100% white (i.e. 255,255,255. 
  928.     // and adj=1 neutral and adj=0 black.
  929.     if( $adj > 1 ) {
  930. $m = ($adj-1.0)*(255-min(255,min($r,min($g,$b))));
  931. return array(min(255,$r+$m), min(255,$g+$m), min(255,$b+$m),$alpha);
  932.     }
  933.     elseif( $adj < 1 ) {
  934. $m = ($adj-1.0)*max(255,max($r,max($g,$b)));
  935. return array(max(0,$r+$m), max(0,$g+$m), max(0,$b+$m),$alpha);
  936.     }
  937.     else {
  938. return array($r,$g,$b,$alpha);
  939.     }
  940. } elseif( is_array($aColor) ) {
  941.     if( count($aColor)==3 ) {
  942. $aColor[3]=0;
  943. return $aColor;
  944.     }
  945.     else
  946. return $aColor;
  947. }
  948. else
  949.     JpGraphError::Raise(" Unknown color specification: $aColor , size=".count($aColor));
  950.     }
  951.     // Compare two colors
  952.     // return true if equal
  953.     function Equal($aCol1,$aCol2) {
  954. $c1 = $this->Color($aCol1);
  955. $c2 = $this->Color($aCol2);
  956. if( $c1[0]==$c2[0] && $c1[1]==$c2[1] && $c1[2]==$c2[2] )
  957.     return true;
  958. else
  959.     return false;
  960.     }
  961.     // Allocate a new color in the current image
  962.     // Return new color index, -1 if no more colors could be allocated
  963.     function Allocate($aColor,$aAlpha=0.0) {
  964. list ($r, $g, $b, $a) = $this->color($aColor);
  965. // If alpha is specified in the color string then this
  966. // takes precedence over the second argument
  967. if( $a > 0 )
  968.     $aAlpha = $a;
  969. if(@$GLOBALS['gd2']==true) {
  970.     if( $aAlpha < 0 || $aAlpha > 1 ) {
  971. JpGraphError::Raise('Alpha parameter for color must be between 0.0 and 1.0');
  972. exit(1);
  973.     }
  974.     return imagecolorresolvealpha($this->img, $r, $g, $b, round($aAlpha * 127));
  975. } else {
  976.     $index = imagecolorexact($this->img, $r, $g, $b);
  977.     if ($index == -1) {
  978.        $index = imagecolorallocate($this->img, $r, $g, $b);
  979.        if( USE_APPROX_COLORS && $index == -1 )
  980.     $index = imagecolorresolve($this->img, $r, $g, $b);
  981.     } 
  982.     return $index;
  983. }
  984.     }
  985. } // Class
  986. //===================================================
  987. // CLASS Image
  988. // Description: Wrapper class with some goodies to form the
  989. // Interface to low level image drawing routines.
  990. //===================================================
  991. class Image {
  992.     var $img_format;
  993.     var $expired=true;
  994.     var $img;
  995.     var $left_margin=30,$right_margin=30,$top_margin=20,$bottom_margin=30;
  996.     var $plotwidth=0,$plotheight=0;
  997.     var $rgb=null;
  998.     var $current_color,$current_color_name;
  999.     var $lastx=0, $lasty=0;
  1000.     var $width, $height;
  1001.     var $line_weight=1;
  1002.     var $line_style=1; // Default line style is solid
  1003.     var $obs_list=array();
  1004.     var $font_size=12,$font_family=FF_FONT1, $font_style=FS_NORMAL;
  1005.     var $font_file='';
  1006.     var $text_halign="left",$text_valign="bottom";
  1007.     var $ttf=null;
  1008.     var $use_anti_aliasing=false;
  1009.     var $quality=null;
  1010.     var $colorstack=array(),$colorstackidx=0;
  1011.     var $canvascolor = 'white' ;
  1012.     var $langconv = null ;
  1013.     //---------------
  1014.     // CONSTRUCTOR
  1015.     function Image($aWidth,$aHeight,$aFormat=DEFAULT_GFORMAT) {
  1016. $this->CreateImgCanvas($aWidth,$aHeight);
  1017. $this->SetAutoMargin();
  1018. if( !$this->SetImgFormat($aFormat) ) {
  1019.     JpGraphError::Raise("JpGraph: Selected graphic format is either not supported or unknown [$aFormat]");
  1020. }
  1021. $this->ttf = new TTF();
  1022. $this->langconv = new LanguageConv();
  1023.     }
  1024.     // Should we use anti-aliasing. Note: This really slows down graphics!
  1025.     function SetAntiAliasing() {
  1026. $this->use_anti_aliasing=true;
  1027.     }
  1028.     function CreateRawCanvas($aWidth=0,$aHeight=0) {
  1029. if( @$GLOBALS['gd2']==true && USE_TRUECOLOR ) {
  1030.     $this->img = @imagecreatetruecolor($aWidth, $aHeight);
  1031.     if( $this->img < 1 ) {
  1032. die("<font color=red><b>JpGraph Error:</b></font> Can't create truecolor image. Check that you really have GD2 library installed.");
  1033.     }
  1034.     $this->SetAlphaBlending();
  1035. } else {
  1036.     $this->img = @imagecreate($aWidth, $aHeight);
  1037.     if( $this->img < 1 ) {
  1038. die("<font color=red><b>JpGraph Error:</b></font> Can't create image. Check that you really have the GD library installed.");
  1039.     }
  1040. }
  1041. if( $this->rgb != null ) 
  1042.     $this->rgb->img = $this->img ;
  1043. else
  1044.     $this->rgb = new RGB($this->img);
  1045.     }
  1046.     function CloneCanvasH() {
  1047. $oldimage = $this->img;
  1048. $this->CreateRawCanvas($this->width,$this->height);
  1049. imagecopy($this->img,$oldimage,0,0,0,0,$this->width,$this->height);
  1050. return $oldimage;
  1051.     }
  1052.     
  1053.     function CreateImgCanvas($aWidth=0,$aHeight=0) {
  1054. $old = array($this->img,$this->width,$this->height);
  1055. $aWidth = round($aWidth);
  1056. $aHeight = round($aHeight);
  1057. $this->width=$aWidth;
  1058. $this->height=$aHeight;
  1059. if( $aWidth==0 || $aHeight==0 ) {
  1060.     // We will set the final size later. 
  1061.     // Note: The size must be specified before any other
  1062.     // img routines that stroke anything are called.
  1063.     $this->img = null;
  1064.     $this->rgb = null;
  1065.     return $old;
  1066. }
  1067. $this->CreateRawCanvas($aWidth,$aHeight);
  1068. // Set canvas color (will also be the background color for a 
  1069. // a pallett image
  1070. $this->SetColor($this->canvascolor);
  1071. $this->FilledRectangle(0,0,$aWidth,$aHeight);
  1072. return $old ;
  1073.     }
  1074.     function CopyCanvasH($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY,$aWidth,$aHeight,$aw=-1,$ah=-1) {
  1075. if( $aw === -1 ) {
  1076.     $aw = $aWidth;
  1077.     $ah = $aHeight;
  1078.     $f = 'imagecopyresized';
  1079. }
  1080. else {
  1081.     $f = $GLOBALS['copyfunc'] ;
  1082. }
  1083. $f($aToHdl,$aFromHdl,
  1084.    $aToX,$aToY,$aFromX,$aFromY, $aWidth,$aHeight,$aw,$ah);
  1085.     }
  1086.     function SetCanvasH($aHdl) {
  1087. $this->img = $aHdl;
  1088. $this->rgb->img = $aHdl;
  1089.     }
  1090.     function SetCanvasColor($aColor) {
  1091. $this->canvascolor = $aColor ;
  1092.     }
  1093.     function SetAlphaBlending($aFlg=true) {
  1094. if( $GLOBALS['gd2'] )
  1095.     ImageAlphaBlending($this->img,$aFlg);
  1096. else 
  1097.     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.');
  1098.     }
  1099.     function SetAutoMargin() {
  1100. GLOBAL $gJpgBrandTiming;
  1101. $min_bm=0;
  1102. if( $gJpgBrandTiming )
  1103.     $min_bm=15;
  1104. $lm = max(0,$this->width/7);
  1105. $rm = max(0,$this->width/10);
  1106. $tm = max(0,$this->height/7);
  1107. $bm = max($min_bm,$this->height/7);
  1108. $this->SetMargin($lm,$rm,$tm,$bm);
  1109.     }
  1110.     //---------------
  1111.     // PUBLIC METHODS
  1112.     // Add observer. The observer will be notified when
  1113.     // the margin changes
  1114.     function AddObserver($aMethod,&$aObject) {
  1115. $this->obs_list[]=array($aMethod,&$aObject);
  1116.     }
  1117.     // Call all observers
  1118.     function NotifyObservers() {
  1119. // foreach($this->obs_list as $o)
  1120. // $o[1]->$o[0]($this);
  1121. for($i=0; $i < count($this->obs_list); ++$i) {
  1122.     $obj = & $this->obs_list[$i][1];
  1123.     $method = $this->obs_list[$i][0];
  1124.     $obj->$method($this);
  1125. }
  1126.     }
  1127.     function SetFont($family,$style=FS_NORMAL,$size=10) {
  1128. if($family==FONT1_BOLD || $family==FONT2_BOLD || $family==FONT0 || $family==FONT1 || $family==FONT2 )
  1129.     JpGraphError::Raise(" Usage of FONT0, FONT1, FONT2 is deprecated. Use FF_xxx instead.");
  1130. $this->font_family=$family;
  1131. $this->font_style=$style;
  1132. $this->font_size=$size;
  1133. $this->font_file='';
  1134. if( ($this->font_family==FF_FONT1 || $this->font_family==FF_FONT2) && $this->font_style==FS_BOLD ){
  1135.     ++$this->font_family;
  1136. }
  1137. if( $this->font_family > FF_FONT2+1 ) { // A TTF font so get the font file
  1138.     // Check that this PHP has support for TTF fonts
  1139.     if( !function_exists('imagettfbbox') ) {
  1140. JpGraphError::Raise('This PHP build has not been configured with TTF support. You need to recompile your PHP installation with FreeType support.');
  1141. exit();
  1142.     }
  1143.     $this->font_file = $this->ttf->File($this->font_family,$this->font_style);
  1144. }
  1145.     }
  1146.     // Get the specific height for a text string
  1147.     function GetTextHeight($txt="",$angle=0) {
  1148. $tmp = split("n",$txt);
  1149. $n = count($tmp);
  1150. $m=0;
  1151. for($i=0; $i< $n; ++$i)
  1152.     $m = max($m,strlen($tmp[$i]));
  1153. if( $this->font_family <= FF_FONT2+1 ) {
  1154.     if( $angle==0 )
  1155. return $n*imagefontheight($this->font_family);
  1156.     else 
  1157. return $m*imagefontwidth($this->font_family);
  1158. }
  1159. else {
  1160.     $bbox = $this->GetTTFBBox($txt,$angle);
  1161.     return $bbox[1]-$bbox[5];
  1162. }
  1163.     }
  1164.     // Estimate font height
  1165.     function GetFontHeight($angle=0) {
  1166. $txt = "XOMg";
  1167. return $this->GetTextHeight($txt,$angle);
  1168.     }
  1169.     // Approximate font width with width of letter "O"
  1170.     function GetFontWidth($angle=0) {
  1171. $txt = 'O';
  1172. return $this->GetTextWidth($txt,$angle);
  1173.     }
  1174.     // Get actual width of text in absolute pixels
  1175.     function GetTextWidth($txt,$angle=0) {
  1176. $tmp = split("n",$txt);
  1177. $n = count($tmp);
  1178. if( $this->font_family <= FF_FONT2+1 ) {
  1179.     $m=0;
  1180.     for($i=0; $i < $n; ++$i) {
  1181. $l=strlen($tmp[$i]);
  1182. if( $l > $m ) {
  1183.     $m = $l;
  1184. }
  1185.     }
  1186.     if( $angle==0 ) {
  1187. $width=$m*imagefontwidth($this->font_family);
  1188. return $width;
  1189.     }
  1190.     else {
  1191. // 90 degrees internal so height becomes width
  1192. return $n*imagefontheight($this->font_family); 
  1193.     }
  1194. }
  1195. else {
  1196.     // For TTF fonts we must walk through a lines and find the 
  1197.     // widest one which we use as the width of the multi-line
  1198.     // paragraph
  1199.     $m=0;
  1200.     for( $i=0; $i < $n; ++$i ) {
  1201. $bbox = $this->GetTTFBBox($tmp[$i],$angle);
  1202. $mm =  $bbox[2] - $bbox[0];
  1203. if( $mm > $m ) 
  1204.     $m = $mm;
  1205.     }
  1206.     return $m;
  1207. }
  1208.     }
  1209.     // Draw text with a box around it
  1210.     function StrokeBoxedText($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black",
  1211.      $shadowcolor=false,$paragraph_align="left",
  1212.      $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) {
  1213. if( !is_numeric($dir) ) {
  1214.     if( $dir=="h" ) $dir=0;
  1215.     elseif( $dir=="v" ) $dir=90;
  1216.     else JpGraphError::Raise(" Unknown direction specified in call to StrokeBoxedText() [$dir]");
  1217. }
  1218. if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
  1219.     $width=$this->GetTextWidth($txt,$dir) ;
  1220.     $height=$this->GetTextHeight($txt,$dir) ;
  1221. }
  1222. else {
  1223.     $width=$this->GetBBoxWidth($txt,$dir) ;
  1224.     $height=$this->GetBBoxHeight($txt,$dir) ;
  1225. }
  1226. $height += 2*$ymarg;
  1227. $width  += 2*$xmarg;
  1228. if( $this->text_halign=="right" ) $x -= $width;
  1229. elseif( $this->text_halign=="center" ) $x -= $width/2;
  1230. if( $this->text_valign=="bottom" ) $y -= $height;
  1231. elseif( $this->text_valign=="center" ) $y -= $height/2;
  1232. if( $shadowcolor ) {
  1233.     $this->PushColor($shadowcolor);
  1234.     $this->FilledRoundedRectangle($x-$xmarg+$dropwidth,$y-$ymarg+$dropwidth,
  1235.   $x+$width+$dropwidth,$y+$height+$dropwidth,
  1236.   $cornerradius);
  1237.     $this->PopColor();
  1238.     $this->PushColor($fcolor);
  1239.     $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
  1240.     $this->PopColor();
  1241.     $this->PushColor($bcolor);
  1242.     $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
  1243.     $this->PopColor();
  1244. }
  1245. else {
  1246.     if( $fcolor ) {
  1247. $oc=$this->current_color;
  1248. $this->SetColor($fcolor);
  1249. $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
  1250. $this->current_color=$oc;
  1251.     }
  1252.     if( $bcolor ) {
  1253. $oc=$this->current_color;
  1254. $this->SetColor($bcolor);
  1255. $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height,$cornerradius);
  1256. $this->current_color=$oc;
  1257.     }
  1258. }
  1259. $h=$this->text_halign;
  1260. $v=$this->text_valign;
  1261. $this->SetTextAlign("left","top");
  1262. $this->StrokeText($x, $y, $txt, $dir, $paragraph_align);
  1263. $this->SetTextAlign($h,$v);
  1264.     }
  1265.     // Set text alignment
  1266.     function SetTextAlign($halign,$valign="bottom") {
  1267. $this->text_halign=$halign;
  1268. $this->text_valign=$valign;
  1269.     }
  1270.     function _StrokeBuiltinFont($x,$y,$txt,$dir=0,$paragraph_align="left") {
  1271. if( is_numeric($dir) && $dir!=90 && $dir!=0) 
  1272.     JpGraphError::Raise(" Internal font does not support drawing text at arbitrary angle. Use TTF fonts instead.");
  1273. $h=$this->GetTextHeight($txt);
  1274. $fh=$this->GetFontHeight();
  1275. $w=$this->GetTextWidth($txt);
  1276. if( $this->text_halign=="right") 
  1277.     $x -= $dir==0 ? $w : $h;
  1278. elseif( $this->text_halign=="center" ) {
  1279.     // For center we subtract 1 pixel since this makes the middle
  1280.     // be prefectly in the middle
  1281.     $x -= $dir==0 ? $w/2-1 : $h/2;
  1282. }
  1283. if( $this->text_valign=="top" )
  1284.     $y += $dir==0 ? $h : $w;
  1285. elseif( $this->text_valign=="center" ) 
  1286.     $y += $dir==0 ? $h/2 : $w/2;
  1287. if( $dir==90 )
  1288.     imagestringup($this->img,$this->font_family,$x,$y,$txt,$this->current_color);
  1289. else {
  1290.     if( ereg("n",$txt) ) { 
  1291. $tmp = split("n",$txt);
  1292. for($i=0; $i < count($tmp); ++$i) {
  1293.     $w1 = $this->GetTextWidth($tmp[$i]);
  1294.     if( $paragraph_align=="left" ) {
  1295. imagestring($this->img,$this->font_family,$x,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
  1296.     }
  1297.     elseif( $paragraph_align=="right" ) {
  1298. imagestring($this->img,$this->font_family,$x+($w-$w1),
  1299.     $y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
  1300.     }
  1301.     else {
  1302. imagestring($this->img,$this->font_family,$x+$w/2-$w1/2,
  1303.     $y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
  1304.     }
  1305. }
  1306.     } 
  1307.     else {
  1308. //Put the text
  1309. imagestring($this->img,$this->font_family,$x,$y-$h+1,$txt,$this->current_color);
  1310.     }
  1311. }
  1312.     }
  1313.     function AddTxtCR($aTxt) {
  1314. // If the user has just specified a 'n'
  1315. // instead of 'nt' we have to add 'r' since
  1316. // the width will be too muchy otherwise since when
  1317. // we print we stroke the individually lines by hand.
  1318. $e = explode("n",$aTxt);
  1319. $n = count($e);
  1320. for($i=0; $i<$n; ++$i) {
  1321.     $e[$i]=str_replace("r","",$e[$i]);
  1322. }
  1323. return implode("nr",$e);
  1324.     }
  1325.     function GetTTFBBox($aTxt,$aAngle=0) {
  1326. $bbox = @ImageTTFBBox($this->font_size,$aAngle,$this->font_file,$aTxt);
  1327. if( $bbox === false ) {
  1328.     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.");
  1329. }
  1330. return $bbox;
  1331.     }
  1332.     function GetBBoxTTF($aTxt,$aAngle=0) {
  1333. // Normalize the bounding box to become a minimum
  1334. // enscribing rectangle
  1335. $aTxt = $this->AddTxtCR($aTxt);
  1336. if( !is_readable($this->font_file) ) {
  1337.     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.');
  1338. }
  1339. $bbox = $this->GetTTFBBox($aTxt,$aAngle);
  1340. if( $aAngle==0 ) 
  1341.     return $bbox;
  1342. if( $aAngle >= 0 ) {
  1343.     if(  $aAngle <= 90 ) { //<=0
  1344. $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
  1345.       $bbox[2],$bbox[5],$bbox[6],$bbox[5]);
  1346.     }
  1347.     elseif(  $aAngle <= 180 ) { //<= 2
  1348. $bbox = array($bbox[4],$bbox[7],$bbox[0],$bbox[7],
  1349.       $bbox[0],$bbox[3],$bbox[4],$bbox[3]);
  1350.     }
  1351.     elseif(  $aAngle <= 270 )  { //<= 3
  1352. $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
  1353.       $bbox[6],$bbox[1],$bbox[2],$bbox[1]);
  1354.     }
  1355.     else {
  1356. $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
  1357.       $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
  1358.     }
  1359. }
  1360. elseif(  $aAngle < 0 ) {
  1361.     if( $aAngle <= -270 ) { // <= -3
  1362. $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
  1363.       $bbox[2],$bbox[5],$bbox[6],$bbox[5]);
  1364.     }
  1365.     elseif( $aAngle <= -180 ) { // <= -2
  1366. $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
  1367.       $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
  1368.     }
  1369.     elseif( $aAngle <= -90 ) { // <= -1
  1370. $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
  1371.       $bbox[6],$bbox[1],$bbox[2],$bbox[1]);
  1372.     }
  1373.     else {
  1374. $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
  1375.       $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
  1376.     }
  1377. }
  1378. return $bbox;
  1379.     }
  1380.     function GetBBoxHeight($aTxt,$aAngle=0) {
  1381. $box = $this->GetBBoxTTF($aTxt,$aAngle);
  1382. return $box[1]-$box[7]+1;
  1383.     }
  1384.     function GetBBoxWidth($aTxt,$aAngle=0) {
  1385. $box = $this->GetBBoxTTF($aTxt,$aAngle);
  1386. return $box[2]-$box[0]+1;
  1387.     }
  1388.     function _StrokeTTF($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
  1389. // Remember the anchor point before adjustment
  1390. if( $debug ) {
  1391.     $ox=$x;
  1392.     $oy=$y;
  1393. }
  1394. if( !ereg("n",$txt) || ($dir>0 && ereg("n",$txt)) ) {
  1395.     // Format a single line
  1396.     $txt = $this->AddTxtCR($txt);
  1397.     $bbox=$this->GetBBoxTTF($txt,$dir);
  1398.     
  1399.     // Align x,y ot lower left corner of bbox
  1400.     $x -= $bbox[0];
  1401.     $y -= $bbox[1];
  1402.     // Note to self: "topanchor" is deprecated after we changed the
  1403.     // bopunding box stuff. 
  1404.     if( $this->text_halign=="right" || $this->text_halign=="topanchor" ) 
  1405. $x -= $bbox[2]-$bbox[0];
  1406.     elseif( $this->text_halign=="center" ) $x -= ($bbox[2]-$bbox[0])/2; 
  1407.     
  1408.     if( $this->text_valign=="top" ) $y += abs($bbox[5])+$bbox[1];
  1409.     elseif( $this->text_valign=="center" ) $y -= ($bbox[5]-$bbox[1])/2; 
  1410.     ImageTTFText ($this->img, $this->font_size, $dir, $x, $y, 
  1411.   $this->current_color,$this->font_file,$txt); 
  1412.             if( $debug ) {
  1413. // Draw the bounding rectangle and the bounding box
  1414. $box=ImageTTFBBox($this->font_size,$dir,$this->font_file,$txt);
  1415. $p = array();
  1416. $p1 = array();
  1417. for($i=0; $i < 4; ++$i) {
  1418.     $p[] = $bbox[$i*2]+$x;
  1419.     $p[] = $bbox[$i*2+1]+$y;
  1420.     $p1[] = $box[$i*2]+$x;
  1421.     $p1[] = $box[$i*2+1]+$y;
  1422. }
  1423. // Draw bounding box
  1424. $this->PushColor('green');
  1425. $this->Polygon($p1,true);
  1426. $this->PopColor();
  1427. // Draw bounding rectangle
  1428. $this->PushColor('darkgreen');
  1429. $this->Polygon($p,true);
  1430. $this->PopColor();
  1431. // Draw a cross at the anchor point
  1432. $this->PushColor('red');
  1433. $this->Line($ox-15,$oy,$ox+15,$oy);
  1434. $this->Line($ox,$oy-15,$ox,$oy+15);
  1435. $this->PopColor();
  1436.             }
  1437. }
  1438. else {
  1439.     // Format a text paragraph
  1440.     $fh=$this->GetFontHeight();
  1441.     // Line margin is 15% of font height
  1442.     $linemargin=round($fh*0.15);
  1443.     $fh += $linemargin;
  1444.     $w=$this->GetTextWidth($txt);
  1445.     $y -= $linemargin/2;
  1446.     $tmp = split("n",$txt);
  1447.     $nl = count($tmp);
  1448.     $h = $nl * $fh;
  1449.     if( $this->text_halign=="right") 
  1450. $x -= $dir==0 ? $w : $h;
  1451.     elseif( $this->text_halign=="center" ) {
  1452. $x -= $dir==0 ? $w/2 : $h/2;
  1453.     }
  1454.     
  1455.     if( $this->text_valign=="top" )
  1456. $y += $dir==0 ? $h : $w;
  1457.     elseif( $this->text_valign=="center" ) 
  1458. $y += $dir==0 ? $h/2 : $w/2;
  1459.     // Here comes a tricky bit. 
  1460.     // Since we have to give the position for the string at the
  1461.     // baseline this means thaht text will move slightly up
  1462.     // and down depending on any of it's character descend below
  1463.     // the baseline, for example a 'g'. To adjust the Y-position
  1464.     // we therefore adjust the text with the baseline Y-offset
  1465.     // as used for the current font and size. This will keep the
  1466.     // baseline at a fixed positoned disregarding the actual 
  1467.     // characters in the string. 
  1468.     $standardbox = $this->GetTTFBBox('Gg',$dir);
  1469.     $yadj = $standardbox[1];
  1470.     $xadj = $standardbox[0];
  1471.     for($i=0; $i < $nl; ++$i) {
  1472. $wl = $this->GetTextWidth($tmp[$i]);
  1473. $bbox = $this->GetTTFBBox($tmp[$i],$dir);
  1474. if( $paragraph_align=="left" ) {
  1475.     $xl = $x; 
  1476. }
  1477. elseif( $paragraph_align=="right" ) {
  1478.     $xl = $x + ($w-$wl);
  1479. }
  1480. else {
  1481.     // Center
  1482.     $xl = $x + $w/2 - $wl/2 ;
  1483. }
  1484. $xl -= $bbox[0];
  1485. $yl = $y  - $yadj; 
  1486. $xl = $xl  - $xadj; 
  1487. ImageTTFText ($this->img, $this->font_size, $dir, 
  1488.       $xl, $yl-($h-$fh)+$fh*$i,
  1489.       $this->current_color,$this->font_file,$tmp[$i]); 
  1490. if( $debug  ) {
  1491.     // Draw the bounding rectangle around each line
  1492.     $box=ImageTTFBBox($this->font_size,$dir,$this->font_file,$tmp[$i]);
  1493.     $p = array();
  1494.     for($j=0; $j < 4; ++$j) {
  1495. $p[] = $bbox[$j*2]+$xl;
  1496. $p[] = $bbox[$j*2+1]+$yl-($h-$fh)+$fh*$i;
  1497.     }
  1498.     
  1499.     // Draw bounding rectangle
  1500.     $this->PushColor('darkgreen');
  1501.     $this->Polygon($p,true);
  1502.     $this->PopColor();
  1503. }
  1504.     }
  1505.     if( $debug ) {
  1506. // Draw a cross at the anchor point
  1507. $this->PushColor('red');
  1508. $this->Line($ox-25,$oy,$ox+25,$oy);
  1509. $this->Line($ox,$oy-25,$ox,$oy+25);
  1510. $this->PopColor();
  1511.     }
  1512. }
  1513.     }
  1514.     function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
  1515. $x = round($x);
  1516. $y = round($y);
  1517. // Do special language encoding
  1518. $txt = $this->langconv->Convert($txt,$this->font_family);
  1519. if( !is_numeric($dir) )
  1520.     JpGraphError::Raise(" Direction for text most be given as an angle between 0 and 90.");
  1521. if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
  1522.     $this->_StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,$debug);
  1523. }
  1524. elseif($this->font_family >= FF_COURIER && $this->font_family <= FF_BOOK)  { 
  1525.     $this->_StrokeTTF($x,$y,$txt,$dir,$paragraph_align,$debug);
  1526. }
  1527. else
  1528.     JpGraphError::Raise(" Unknown font font family specification. ");
  1529.     }
  1530.     function SetMargin($lm,$rm,$tm,$bm) {
  1531. $this->left_margin=$lm;
  1532. $this->right_margin=$rm;
  1533. $this->top_margin=$tm;
  1534. $this->bottom_margin=$bm;
  1535. $this->plotwidth=$this->width - $this->left_margin-$this->right_margin ; 
  1536. $this->plotheight=$this->height - $this->top_margin-$this->bottom_margin ;
  1537. if( $this->plotwidth < 0  || $this->plotheight < 0 )
  1538.     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.");
  1539. $this->NotifyObservers();
  1540.     }
  1541.     function SetTransparent($color) {
  1542. imagecolortransparent ($this->img,$this->rgb->allocate($color));
  1543.     }
  1544.     function SetColor($color,$aAlpha=0) {
  1545. $this->current_color_name = $color;
  1546. $this->current_color=$this->rgb->allocate($color,$aAlpha);
  1547. if( $this->current_color == -1 ) {
  1548.     $tc=imagecolorstotal($this->img);
  1549.     JpGraphError::Raise("Can't allocate any more colors.
  1550. Image has already allocated maximum of <b>$tc colors</b>. 
  1551. This might happen if you have anti-aliasing turned on
  1552. together with a background image or perhaps gradient fill 
  1553. since this requires many, many colors. Try to turn off
  1554. anti-aliasing.<p>
  1555. If there is still a problem try downgrading the quality of
  1556. the background image to use a smaller pallete to leave some 
  1557. entries for your graphs. You should try to limit the number
  1558. of colors in your background image to 64.<p>
  1559. If there is still problem set the constant 
  1560. <pre>
  1561. DEFINE("USE_APPROX_COLORS",true);
  1562. </pre>
  1563. in jpgraph.php This will use approximative colors
  1564. when the palette is full.
  1565. <p>
  1566. Unfortunately there is not much JpGraph can do about this
  1567. since the palette size is a limitation of current graphic format and
  1568. what the underlying GD library suppports."); 
  1569. }
  1570. return $this->current_color;
  1571.     }
  1572.     function PushColor($color) {
  1573. if( $color != "" ) {
  1574.     $this->colorstack[$this->colorstackidx]=$this->current_color_name;
  1575.     $this->colorstack[$this->colorstackidx+1]=$this->current_color;
  1576.     $this->colorstackidx+=2;
  1577.     $this->SetColor($color);
  1578. }
  1579. else {
  1580.     JpGraphError::Raise("Color specified as empty string in PushColor().");
  1581. }
  1582.     }
  1583.     function PopColor() {
  1584. if($this->colorstackidx<1)
  1585.     JpGraphError::Raise(" Negative Color stack index. Unmatched call to PopColor()");
  1586. $this->current_color=$this->colorstack[--$this->colorstackidx];
  1587. $this->current_color_name=$this->colorstack[--$this->colorstackidx];
  1588.     }
  1589.     // Why this duplication? Because this way we can call this method
  1590.     // for any image and not only the current objsct
  1591.     function AdjSat($sat) { $this->_AdjSat($this->img,$sat); }
  1592.     function _AdjSat($img,$sat) {
  1593. $nbr = imagecolorstotal ($img);
  1594. for( $i=0; $i<$nbr; ++$i ) {
  1595.     $colarr = imagecolorsforindex ($img,$i);
  1596.     $rgb[0]=$colarr["red"];
  1597.     $rgb[1]=$colarr["green"];
  1598.     $rgb[2]=$colarr["blue"];
  1599.     $rgb = $this->AdjRGBSat($rgb,$sat);
  1600.     imagecolorset ($img, $i, $rgb[0], $rgb[1], $rgb[2]);
  1601. }
  1602.     }
  1603.     function AdjBrightContrast($bright,$contr=0) {
  1604. $this->_AdjBrightContrast($this->img,$bright,$contr);
  1605.     }
  1606.     function _AdjBrightContrast($img,$bright,$contr=0) {
  1607. if( $bright < -1 || $bright > 1 || $contr < -1 || $contr > 1 )
  1608.     JpGraphError::Raise(" Parameters for brightness and Contrast out of range [-1,1]");
  1609. $nbr = imagecolorstotal ($img);
  1610. for( $i=0; $i<$nbr; ++$i ) {
  1611.     $colarr = imagecolorsforindex ($img,$i);
  1612.     $r = $this->AdjRGBBrightContrast($colarr["red"],$bright,$contr);
  1613.     $g = $this->AdjRGBBrightContrast($colarr["green"],$bright,$contr);
  1614.     $b = $this->AdjRGBBrightContrast($colarr["blue"],$bright,$contr);
  1615.     imagecolorset ($img, $i, $r, $g, $b);
  1616. }
  1617.     }
  1618.     // Private helper function for adj sat
  1619.     // Adjust saturation for RGB array $u. $sat is a value between -1 and 1
  1620.     // Note: Due to GD inability to handle true color the RGB values are only between
  1621.     // 8 bit. This makes saturation quite sensitive for small increases in parameter sat.
  1622.     // 
  1623.     // Tip: To get a grayscale picture set sat=-100, values <-100 changes the colors
  1624.     // to it's complement.
  1625.     // 
  1626.     // Implementation note: The saturation is implemented directly in the RGB space
  1627.     // by adjusting the perpendicular distance between the RGB point and the "grey"
  1628.     // line (1,1,1). Setting $sat>0 moves the point away from the line along the perp.
  1629.     // distance and a negative value moves the point closer to the line.
  1630.     // The values are truncated when the color point hits the bounding box along the
  1631.     // RGB axis.
  1632.     // DISCLAIMER: I'm not 100% sure this is he correct way to implement a color 
  1633.     // saturation function in RGB space. However, it looks ok and has the expected effect.
  1634.     function AdjRGBSat($rgb,$sat) {
  1635. // TODO: Should be moved to the RGB class
  1636. // Grey vector
  1637. $v=array(1,1,1);
  1638. // Dot product
  1639. $dot = $rgb[0]*$v[0]+$rgb[1]*$v[1]+$rgb[2]*$v[2];
  1640. // Normalize dot product
  1641. $normdot = $dot/3; // dot/|v|^2
  1642. // Direction vector between $u and its projection onto $v
  1643. for($i=0; $i<3; ++$i)
  1644.     $r[$i] = $rgb[$i] - $normdot*$v[$i];
  1645. // Adjustment factor so that sat==1 sets the highest RGB value to 255
  1646. if( $sat > 0 ) {
  1647.     $m=0;
  1648.     for( $i=0; $i<3; ++$i) {
  1649. if( sign($r[$i]) == 1 && $r[$i]>0)
  1650.     $m=max($m,(255-$rgb[$i])/$r[$i]);
  1651.     }
  1652.     $tadj=$m;
  1653. }
  1654. else
  1655.     $tadj=1;
  1656. $tadj = $tadj*$sat;
  1657. for($i=0; $i<3; ++$i) {
  1658.     $un[$i] = round($rgb[$i] + $tadj*$r[$i]);
  1659.     if( $un[$i]<0 ) $un[$i]=0; // Truncate color when they reach 0
  1660.     if( $un[$i]>255 ) $un[$i]=255;// Avoid potential rounding error
  1661. }
  1662. return $un;
  1663.     }
  1664.     // Private helper function for AdjBrightContrast
  1665.     function AdjRGBBrightContrast($rgb,$bright,$contr) {
  1666. // TODO: Should be moved to the RGB class
  1667. // First handle contrast, i.e change the dynamic range around grey
  1668. if( $contr <= 0 ) {
  1669.     // Decrease contrast
  1670.     $adj = abs($rgb-128) * (-$contr);
  1671.     if( $rgb < 128 ) $rgb += $adj;
  1672.     else $rgb -= $adj;
  1673. }
  1674. else { // $contr > 0
  1675.     // Increase contrast
  1676.     if( $rgb < 128 ) $rgb = $rgb - ($rgb * $contr);
  1677.     else $rgb = $rgb + ((255-$rgb) * $contr);
  1678. }
  1679. // Add (or remove) various amount of white
  1680. $rgb += $bright*255;
  1681. $rgb=min($rgb,255);
  1682. $rgb=max($rgb,0);
  1683. return $rgb;
  1684.     }
  1685.     function SetLineWeight($weight) {
  1686. $this->line_weight = $weight;
  1687.     }
  1688.     function SetStartPoint($x,$y) {
  1689. $this->lastx=round($x);
  1690. $this->lasty=round($y);
  1691.     }
  1692.     function Arc($cx,$cy,$w,$h,$s,$e) {
  1693. // GD Arc doesn't like negative angles
  1694. while( $s < 0) $s += 360;
  1695. while( $e < 0) $e += 360;
  1696.     
  1697. imagearc($this->img,round($cx),round($cy),round($w),round($h),
  1698.  $s,$e,$this->current_color);
  1699.     }
  1700.     
  1701.     function FilledArc($xc,$yc,$w,$h,$s,$e,$style="") {
  1702. if( $GLOBALS['gd2'] ) {
  1703.     while( $s < 0 ) $s += 360;
  1704.     while( $e < 0 ) $e += 360;
  1705.     if( $style=="" ) 
  1706. $style=IMG_ARC_PIE;
  1707.     imagefilledarc($this->img,round($xc),round($yc),round($w),round($h),
  1708.    round($s),round($e),$this->current_color,$style);
  1709.     return;
  1710. }
  1711. // In GD 1.x we have to do it ourself interesting enough there is surprisingly
  1712. // little difference in time between doing it PHP and using the optimised GD 
  1713. // library (roughly ~20%) I had expected it to be at least 100% slower doing it
  1714. // manually with a polygon approximation in PHP.....
  1715. $fillcolor = $this->current_color_name;
  1716. $w /= 2; // We use radius in our calculations instead
  1717. $h /= 2;
  1718. // Setup the angles so we have the same conventions as the builtin
  1719. // FilledArc() which is a little bit strange if you ask me....
  1720. $s = 360-$s;
  1721. $e = 360-$e;
  1722. if( $e > $s ) {
  1723.     $e = $e - 360;
  1724.     $da = $s - $e; 
  1725. }
  1726. $da = $s-$e;
  1727. // We use radians
  1728. $s *= M_PI/180;
  1729. $e *= M_PI/180;
  1730. $da *= M_PI/180;
  1731. // Calculate a polygon approximation
  1732. $p[0] = $xc;
  1733. $p[1] = $yc;
  1734. // Heuristic on how many polygons we need to make the
  1735. // arc look good
  1736. $numsteps = round(8 * abs($da) * ($w+$h)*($w+$h)/1500);
  1737. if( $numsteps == 0 ) return;
  1738. if( $numsteps < 7 ) $numsteps=7;
  1739. $delta = abs($da)/$numsteps;
  1740. $pa=array();
  1741. $a = $s;
  1742. for($i=1; $i<=$numsteps; ++$i ) {
  1743.     $p[2*$i] = round($xc + $w*cos($a));
  1744.     $p[2*$i+1] = round($yc - $h*sin($a));
  1745.     //$a = $s + $i*$delta; 
  1746.     $a -= $delta; 
  1747.     $pa[2*($i-1)] = $p[2*$i];
  1748.     $pa[2*($i-1)+1] = $p[2*$i+1];
  1749. }
  1750. // Get the last point at the exact ending angle to avoid
  1751. // any rounding errors.
  1752. $p[2*$i] = round($xc + $w*cos($e));
  1753. $p[2*$i+1] = round($yc - $h*sin($e));
  1754. $pa[2*($i-1)] = $p[2*$i];
  1755. $pa[2*($i-1)+1] = $p[2*$i+1];
  1756. $i++;
  1757. $p[2*$i] = $xc;
  1758.      $p[2*$i+1] = $yc;
  1759. if( $fillcolor != "" ) {
  1760.     $this->PushColor($fillcolor);
  1761.     imagefilledpolygon($this->img,$p,count($p)/2,$this->current_color);
  1762.     $this->PopColor();
  1763. }
  1764.     }
  1765.     function FilledCakeSlice($cx,$cy,$w,$h,$s,$e) {
  1766. $this->CakeSlice($cx,$cy,$w,$h,$s,$e,$this->current_color_name);
  1767.     }
  1768.     function CakeSlice($xc,$yc,$w,$h,$s,$e,$fillcolor="",$arccolor="") {
  1769. $s = round($s); $e = round($e);
  1770. $w = round($w); $h = round($h);
  1771. $xc = round($xc); $yc = round($yc);
  1772. $this->PushColor($fillcolor);
  1773. $this->FilledArc($xc,$yc,2*$w,2*$h,$s,$e);
  1774. $this->PopColor();
  1775. if( $arccolor != "" ) {
  1776.     $this->PushColor($arccolor);
  1777.     // We add 2 pixels to make the Arc() better aligned with
  1778.     // the filled arc. 
  1779.     if( $GLOBALS['gd2'] ) {
  1780. imagefilledarc($this->img,$xc,$yc,2*$w,2*$h,$s,$e,$this->current_color_name,
  1781.        IMG_ARC_NOFILL | IMG_ARC_EDGED ) ;
  1782.     }
  1783.     else {
  1784. $this->Arc($xc,$yc,2*$w+2,2*$h+2,$s,$e);
  1785. $xx = $w * cos(2*M_PI - $s*M_PI/180) + $xc;
  1786. $yy = $yc - $h * sin(2*M_PI - $s*M_PI/180);
  1787. $this->Line($xc,$yc,$xx,$yy);
  1788. $xx = $w * cos(2*M_PI - $e*M_PI/180) + $xc;
  1789. $yy = $yc - $h * sin(2*M_PI - $e*M_PI/180);
  1790. $this->Line($xc,$yc,$xx,$yy);
  1791.     }
  1792.     $this->PopColor();
  1793. }
  1794.     }
  1795.     function Ellipse($xc,$yc,$w,$h) {
  1796. $this->Arc($xc,$yc,$w,$h,0,360);
  1797.     }
  1798.     // Breseham circle gives visually better result then using GD
  1799.     // built in arc(). It takes some more time but gives better
  1800.     // accuracy.
  1801.     function BresenhamCircle($xc,$yc,$r) {
  1802. $d = 3-2*$r;
  1803. $x = 0;
  1804. $y = $r;
  1805. while($x<=$y) {
  1806.     $this->Point($xc+$x,$yc+$y);
  1807.     $this->Point($xc+$x,$yc-$y);
  1808.     $this->Point($xc-$x,$yc+$y);
  1809.     $this->Point($xc-$x,$yc-$y);
  1810.     $this->Point($xc+$y,$yc+$x);
  1811.     $this->Point($xc+$y,$yc-$x);
  1812.     $this->Point($xc-$y,$yc+$x);
  1813.     $this->Point($xc-$y,$yc-$x);
  1814.     if( $d<0 ) $d += 4*$x+6;
  1815.     else {
  1816. $d += 4*($x-$y)+10;
  1817. --$y;
  1818.     }
  1819.     ++$x;
  1820. }
  1821.     }
  1822.     function Circle($xc,$yc,$r) {
  1823. if( USE_BRESENHAM )
  1824.     $this->BresenhamCircle($xc,$yc,$r);
  1825. else {
  1826.     /*
  1827.             // Some experimental code snippet to see if we can get a decent 
  1828.     // result doing a trig-circle
  1829.     // Create an approximated circle with 0.05 rad resolution
  1830.     $end = 2*M_PI;
  1831.     $l = $r/10;
  1832.     if( $l < 3 ) $l=3;
  1833.     $step_size = 2*M_PI/(2*$r*M_PI/$l);
  1834.     $pts = array();
  1835.     $pts[] = $r + $xc;
  1836.     $pts[] = $yc;
  1837.     for( $a=$step_size; $a <= $end; $a += $step_size ) {
  1838. $pts[] = round($xc + $r*cos($a));
  1839. $pts[] = round($yc - $r*sin($a));
  1840.     }
  1841.     imagepolygon($this->img,$pts,count($pts)/2,$this->current_color);
  1842.     */
  1843.     $this->Arc($xc,$yc,$r*2,$r*2,0,360);
  1844.     // For some reason imageellipse() isn't in GD 2.0.1, PHP 4.1.1
  1845.     //imageellipse($this->img,$xc,$yc,$r,$r,$this->current_color);
  1846. }
  1847.     }
  1848.     function FilledCircle($xc,$yc,$r) {
  1849. if( $GLOBALS['gd2'] ) {
  1850.     imagefilledellipse($this->img,round($xc),round($yc),
  1851.             2*$r,2*$r,$this->current_color);
  1852. }
  1853. else {
  1854.     for( $i=1; $i < 2*$r; $i += 2 ) {
  1855. // To avoid moire patterns we have to draw some
  1856. // 1 extra "skewed" filled circles
  1857. $this->Arc($xc,$yc,$i,$i,0,360);
  1858. $this->Arc($xc,$yc,$i+1,$i,0,360);
  1859. $this->Arc($xc,$yc,$i+1,$i+1,0,360);
  1860.     }
  1861. }
  1862.     }
  1863.     // Linear Color InterPolation
  1864.     function lip($f,$t,$p) {
  1865. $p = round($p,1);
  1866. $r = $f[0] + ($t[0]-$f[0])*$p;
  1867. $g = $f[1] + ($t[1]-$f[1])*$p;
  1868. $b = $f[2] + ($t[2]-$f[2])*$p;
  1869. return array($r,$g,$b);
  1870.     }
  1871.     // Anti-aliased line. 
  1872.     // Note that this is roughly 8 times slower then a normal line!
  1873.     function WuLine($x1,$y1,$x2,$y2) {
  1874. // Get foreground line color
  1875. $lc = imagecolorsforindex($this->img,$this->current_color);
  1876. $lc = array($lc["red"],$lc["green"],$lc["blue"]);
  1877. $dx = $x2-$x1;
  1878. $dy = $y2-$y1;
  1879. if( abs($dx) > abs($dy) ) {
  1880.     if( $dx<0 ) {
  1881. $dx = -$dx;$dy = -$dy;
  1882. $tmp=$x2;$x2=$x1;$x1=$tmp;
  1883. $tmp=$y2;$y2=$y1;$y1=$tmp;
  1884.     }
  1885.     $x=$x1<<16; $y=$y1<<16;
  1886.     $yinc = ($dy*65535)/$dx;
  1887.     while( ($x >> 16) < $x2 ) {
  1888. $bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
  1889. if( $bc <= 0 ) {
  1890.     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.');
  1891. }
  1892. $bc=array($bc["red"],$bc["green"],$bc["blue"]);
  1893. $this->SetColor($this->lip($lc,$bc,($y & 0xFFFF)/65535));
  1894. imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
  1895. $this->SetColor($this->lip($lc,$bc,(~$y & 0xFFFF)/65535));
  1896. imagesetpixel($this->img,$x>>16,($y>>16)+1,$this->current_color);
  1897. $x += 65536; $y += $yinc;
  1898.     }
  1899. }
  1900. else {
  1901.     if( $dy<0 ) {
  1902. $dx = -$dx;$dy = -$dy;
  1903. $tmp=$x2;$x2=$x1;$x1=$tmp;
  1904. $tmp=$y2;$y2=$y1;$y1=$tmp;
  1905.     }
  1906.     $x=$x1<<16; $y=$y1<<16;
  1907.     $xinc = ($dx*65535)/$dy;
  1908.     while( ($y >> 16) < $y2 ) {
  1909. $bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
  1910. if( $bc <= 0 ) {
  1911.     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.');
  1912. }
  1913. $bc=array($bc["red"],$bc["green"],$bc["blue"]);
  1914. $this->SetColor($this->lip($lc,$bc,($x & 0xFFFF)/65535));
  1915. imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
  1916. $this->SetColor($this->lip($lc,$bc,(~$x & 0xFFFF)/65535));
  1917. imagesetpixel($this->img,($x>>16)+1,$y>>16,$this->current_color);
  1918. $y += 65536; $x += $xinc;
  1919.     }
  1920. }
  1921. $this->SetColor($lc);
  1922. imagesetpixel($this->img,$x2,$y2,$this->current_color);
  1923. imagesetpixel($this->img,$x1,$y1,$this->current_color);
  1924.     }
  1925.     // Set line style dashed, dotted etc
  1926.     function SetLineStyle($s) {
  1927. if( is_numeric($s) ) {
  1928.     if( $s<1 || $s>4 ) 
  1929. JpGraphError::Raise(" Illegal numeric argument to SetLineStyle(): ($s)");
  1930. }
  1931. elseif( is_string($s) ) {
  1932.     if( $s == "solid" ) $s=1;
  1933.     elseif( $s == "dotted" ) $s=2;
  1934.     elseif( $s == "dashed" ) $s=3;
  1935.     elseif( $s == "longdashed" ) $s=4;
  1936.     else JpGraphError::Raise(" Illegal string argument to SetLineStyle(): $s");
  1937. }
  1938. else JpGraphError::Raise(" Illegal argument to SetLineStyle $s");
  1939. $this->line_style=$s;
  1940.     }
  1941.     // Same as Line but take the line_style into account
  1942.     function StyleLine($x1,$y1,$x2,$y2) {
  1943. switch( $this->line_style ) {
  1944.     case 1:// Solid
  1945. $this->Line($x1,$y1,$x2,$y2);
  1946. break;
  1947.     case 2: // Dotted
  1948. $this->DashedLine($x1,$y1,$x2,$y2,1,6);
  1949. break;
  1950.     case 3: // Dashed
  1951. $this->DashedLine($x1,$y1,$x2,$y2,2,4);
  1952. break;
  1953.     case 4: // Longdashes
  1954. $this->DashedLine($x1,$y1,$x2,$y2,8,6);
  1955. break;
  1956.     default:
  1957. JpGraphError::Raise(" Unknown line style: $this->line_style ");
  1958. break;
  1959. }
  1960.     }
  1961.     function Line($x1,$y1,$x2,$y2) {
  1962. $x1 = round($x1);
  1963. $x2 = round($x2);
  1964. $y1 = round($y1);
  1965. $y2 = round($y2);
  1966. if( $this->line_weight==0 ) return;
  1967. if( $this->use_anti_aliasing ) {
  1968.     $dx = $x2-$x1;
  1969.     $dy = $y2-$y1;
  1970.     // Vertical, Horizontal or 45 lines don't need anti-aliasing
  1971.     if( $dx!=0 && $dy!=0 && $dx!=$dy ) {
  1972. $this->WuLine($x1,$y1,$x2,$y2);
  1973. return;
  1974.     }
  1975. }
  1976. if( $this->line_weight==1 ) {
  1977.     imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
  1978. }
  1979. elseif( $x1==$x2 ) { // Special case for vertical lines
  1980.     imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
  1981.     $w1=floor($this->line_weight/2);
  1982.     $w2=floor(($this->line_weight-1)/2);
  1983.     for($i=1; $i<=$w1; ++$i) 
  1984. imageline($this->img,$x1+$i,$y1,$x2+$i,$y2,$this->current_color);
  1985.     for($i=1; $i<=$w2; ++$i) 
  1986. imageline($this->img,$x1-$i,$y1,$x2-$i,$y2,$this->current_color);
  1987. }
  1988. elseif( $y1==$y2 ) { // Special case for horizontal lines
  1989.     imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
  1990.     $w1=floor($this->line_weight/2);
  1991.     $w2=floor(($this->line_weight-1)/2);
  1992.     for($i=1; $i<=$w1; ++$i) 
  1993. imageline($this->img,$x1,$y1+$i,$x2,$y2+$i,$this->current_color);
  1994.     for($i=1; $i<=$w2; ++$i) 
  1995. imageline($this->img,$x1,$y1-$i,$x2,$y2-$i,$this->current_color);
  1996. }
  1997. else { // General case with a line at an angle
  1998.     $a = atan2($y1-$y2,$x2-$x1);
  1999.     // Now establish some offsets from the center. This gets a little
  2000.     // bit involved since we are dealing with integer functions and we
  2001.     // want the apperance to be as smooth as possible and never be thicker
  2002.     // then the specified width.
  2003.     // We do the trig stuff to make sure that the endpoints of the line
  2004.     // are perpendicular to the line itself.
  2005.     $dx=(sin($a)*$this->line_weight/2);
  2006.     $dy=(cos($a)*$this->line_weight/2);
  2007.     $pnts = array($x2+$dx,$y2+$dy,$x2-$dx,$y2-$dy,$x1-$dx,$y1-$dy,$x1+$dx,$y1+$dy);
  2008.     imagefilledpolygon($this->img,$pnts,count($pnts)/2,$this->current_color);
  2009. }
  2010. $this->lastx=$x2; $this->lasty=$y2;
  2011.     }
  2012.     function Polygon($p,$closed=FALSE) {
  2013. if( $this->line_weight==0 ) return;
  2014. $n=count($p);
  2015. $oldx = $p[0];
  2016. $oldy = $p[1];
  2017. for( $i=2; $i < $n; $i+=2 ) {
  2018.     $this->Line($oldx,$oldy,$p[$i],$p[$i+1]);
  2019.     $oldx = $p[$i];
  2020.     $oldy = $p[$i+1];
  2021. }
  2022. if( $closed )
  2023.     $this->Line($oldx,$oldy,$p[0],$p[1]);
  2024.     }
  2025.     function FilledPolygon($pts) {
  2026. $n=count($pts);
  2027. for($i=0; $i < $n; ++$i) 
  2028.     $pts[$i] = round($pts[$i]);
  2029. imagefilledpolygon($this->img,$pts,count($pts)/2,$this->current_color);
  2030.     }
  2031.     function Rectangle($xl,$yu,$xr,$yl) {
  2032. $this->Polygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl,$xl,$yu));
  2033.     }
  2034.     function FilledRectangle($xl,$yu,$xr,$yl) {
  2035. $this->FilledPolygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl));
  2036.     }
  2037.     function FilledRectangle2($xl,$yu,$xr,$yl,$color1,$color2,$style=1) {
  2038. // Fill a rectangle with lines of two colors
  2039. if( $style===1 ) {
  2040.     // Horizontal stripe
  2041.     if( $yl < $yu ) {
  2042. $t = $yl; $yl=$yu; $yu=$t;
  2043.     }
  2044.     for( $y=$yu; $y <= $yl; ++$y) {
  2045. $this->SetColor($color1);
  2046. $this->Line($xl,$y,$xr,$y);
  2047. ++$y;
  2048. $this->SetColor($color2);
  2049. $this->Line($xl,$y,$xr,$y);
  2050.     }
  2051. }
  2052. else {
  2053.     if( $xl < $xl ) {
  2054. $t = $xl; $xl=$xr; $xr=$t;
  2055.     }
  2056.     for( $x=$xl; $x <= $xr; ++$x) {
  2057. $this->SetColor($color1);
  2058. $this->Line($x,$yu,$x,$yl);
  2059. ++$x;
  2060. $this->SetColor($color2);
  2061. $this->Line($x,$yu,$x,$yl);
  2062.     }
  2063. }
  2064.     }
  2065.     function ShadowRectangle($xl,$yu,$xr,$yl,$fcolor=false,$shadow_width=3,$shadow_color=array(102,102,102)) {
  2066. // This is complicated by the fact that we must also handle the case where
  2067.         // the reactangle has no fill color
  2068. $this->PushColor($shadow_color);
  2069. $this->FilledRectangle($xr-$shadow_width,$yu+$shadow_width,$xr,$yl-$shadow_width-1);
  2070. $this->FilledRectangle($xl+$shadow_width,$yl-$shadow_width,$xr,$yl);
  2071. //$this->FilledRectangle($xl+$shadow_width,$yu+$shadow_width,$xr,$yl);
  2072. $this->PopColor();
  2073. if( $fcolor==false )
  2074.     $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
  2075. else {
  2076.     $this->PushColor($fcolor);
  2077.     $this->FilledRectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
  2078.     $this->PopColor();
  2079.     $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
  2080. }
  2081.     }
  2082.     function FilledRoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
  2083. if( $r==0 ) {
  2084.     $this->FilledRectangle($xt,$yt,$xr,$yl);
  2085.     return;
  2086. }
  2087. // To avoid overlapping fillings (which will look strange
  2088. // when alphablending is enabled) we have no choice but 
  2089. // to fill the five distinct areas one by one.
  2090. // Center square
  2091. $this->FilledRectangle($xt+$r,$yt+$r,$xr-$r,$yl-$r);
  2092. // Top band
  2093. $this->FilledRectangle($xt+$r,$yt,$xr-$r,$yt+$r-1);
  2094. // Bottom band
  2095. $this->FilledRectangle($xt+$r,$yl-$r+1,$xr-$r,$yl);
  2096. // Left band
  2097. $this->FilledRectangle($xt,$yt+$r+1,$xt+$r-1,$yl-$r);
  2098. // Right band
  2099. $this->FilledRectangle($xr-$r+1,$yt+$r,$xr,$yl-$r);
  2100. // Topleft & Topright arc
  2101. $this->FilledArc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
  2102. $this->FilledArc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
  2103. // Bottomleft & Bottom right arc
  2104. $this->FilledArc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
  2105. $this->FilledArc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
  2106.     }
  2107.     function RoundedRectangle($xt,$yt,$xr,$yl,$r=5) {    
  2108. if( $r==0 ) {
  2109.     $this->Rectangle($xt,$yt,$xr,$yl);
  2110.     return;
  2111. }
  2112. // Top & Bottom line
  2113. $this->Line($xt+$r,$yt,$xr-$r,$yt);
  2114. $this->Line($xt+$r,$yl,$xr-$r,$yl);
  2115. // Left & Right line
  2116. $this->Line($xt,$yt+$r,$xt,$yl-$r);
  2117. $this->Line($xr,$yt+$r,$xr,$yl-$r);
  2118. // Topleft & Topright arc
  2119. $this->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
  2120. $this->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
  2121. // Bottomleft & Bottomright arc
  2122. $this->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
  2123. $this->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
  2124.     }
  2125.     function FilledBevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='darkgray@0.4') {
  2126. $this->FilledRectangle($x1,$y1,$x2,$y2);
  2127. $this->Bevel($x1,$y1,$x2,$y2,$depth,$color1,$color2);
  2128.     }
  2129.     function Bevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='black@0.5') {
  2130. $this->PushColor($color1);
  2131. for( $i=0; $i < $depth; ++$i ) {
  2132.     $this->Line($x1+$i,$y1+$i,$x1+$i,$y2-$i);
  2133.     $this->Line($x1+$i,$y1+$i,$x2-$i,$y1+$i);
  2134. }
  2135. $this->PopColor();
  2136. $this->PushColor($color2);
  2137. for( $i=0; $i < $depth; ++$i ) {
  2138.     $this->Line($x1+$i,$y2-$i,$x2-$i,$y2-$i);
  2139.     $this->Line($x2-$i,$y1+$i,$x2-$i,$y2-$i-1);
  2140. }
  2141. $this->PopColor();
  2142.     }
  2143.     function StyleLineTo($x,$y) {
  2144. $this->StyleLine($this->lastx,$this->lasty,$x,$y);
  2145. $this->lastx=$x;
  2146. $this->lasty=$y;
  2147.     }
  2148.     function LineTo($x,$y) {
  2149. $this->Line($this->lastx,$this->lasty,$x,$y);
  2150. $this->lastx=$x;
  2151. $this->lasty=$y;
  2152.     }
  2153.     function Point($x,$y) {
  2154. imagesetpixel($this->img,round($x),round($y),$this->current_color);
  2155.     }
  2156.     function Fill($x,$y) {
  2157. imagefill($this->img,round($x),round($y),$this->current_color);
  2158.     }
  2159.     function FillToBorder($x,$y,$aBordColor) {
  2160. $bc = $this->rgb->allocate($aBordColor);
  2161. if( $bc == -1 ) {
  2162.     JpGraphError::Raise('Image::FillToBorder : Can not allocate more colors');
  2163.     exit();
  2164. }
  2165. imagefilltoborder($this->img,round($x),round($y),$bc,$this->current_color);
  2166.     }
  2167.     function DashedLine($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) {
  2168. // Code based on, but not identical to, work by Ariel Garza and James Pine
  2169. $line_length = ceil (sqrt(pow(($x2 - $x1),2) + pow(($y2 - $y1),2)) );
  2170. $dx = ($line_length) ? ($x2 - $x1) / $line_length : 0;
  2171. $dy = ($line_length) ? ($y2 - $y1) / $line_length : 0;
  2172. $lastx = $x1; $lasty = $y1;
  2173. $xmax = max($x1,$x2);
  2174. $xmin = min($x1,$x2);
  2175. $ymax = max($y1,$y2);
  2176. $ymin = min($y1,$y2);
  2177. for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
  2178.     $x = ($dash_length * $dx) + $lastx;
  2179.     $y = ($dash_length * $dy) + $lasty;
  2180.     // The last section might overshoot so we must take a computational hit
  2181.     // and check this.
  2182.     if( $x>$xmax ) $x=$xmax;
  2183.     if( $y>$ymax ) $y=$ymax;
  2184.     if( $x<$xmin ) $x=$xmin;
  2185.     if( $y<$ymin ) $y=$ymin;
  2186.     $this->Line($lastx,$lasty,$x,$y);
  2187.     $lastx = $x + ($dash_space * $dx);
  2188.     $lasty = $y + ($dash_space * $dy);
  2189.     } 
  2190.     function SetExpired($aFlg=true) {
  2191. $this->expired = $aFlg;
  2192.     }
  2193.     // Generate image header
  2194.     function Headers() {
  2195. if ($this->expired) {
  2196.     header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  2197.     header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
  2198.     header("Cache-Control: no-cache, must-revalidate");
  2199.     header("Pragma: no-cache");
  2200. }
  2201. header("Content-type: image/$this->img_format");
  2202.     }
  2203.     // Adjust image quality for formats that allow this
  2204.     function SetQuality($q) {
  2205. $this->quality = $q;
  2206.     }
  2207.     // Stream image to browser or to file
  2208.     function Stream($aFile="") {
  2209. $func="image".$this->img_format;
  2210. if( $this->img_format=="jpeg" && $this->quality != null ) {
  2211.     $res = @$func($this->img,$aFile,$this->quality);
  2212. }
  2213. else {
  2214.     if( $aFile != "" ) {
  2215. $res = @$func($this->img,$aFile);
  2216.     }
  2217.     else
  2218. $res = @$func($this->img);
  2219. }
  2220. if( !$res )
  2221.     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.");
  2222.     }
  2223.     // Clear resource tide up by image
  2224.     function Destroy() {
  2225. imagedestroy($this->img);
  2226.     }
  2227.     // Specify image format. Note depending on your installation
  2228.     // of PHP not all formats may be supported.
  2229.     function SetImgFormat($aFormat) {
  2230. $aFormat = strtolower($aFormat);
  2231. $tst = true;
  2232. $supported = imagetypes();
  2233. if( $aFormat=="auto" ) {
  2234.     if( $supported & IMG_PNG )
  2235. $this->img_format="png";
  2236.     elseif( $supported & IMG_JPG )
  2237. $this->img_format="jpeg";
  2238.     elseif( $supported & IMG_GIF )
  2239. $this->img_format="gif";
  2240.     else
  2241. JpGraphError::Raise(" Your PHP (and GD-lib) installation does not appear to support any known graphic formats.".
  2242.     "You need to first make sure GD is compiled as a module to PHP. If you also want to use JPEG images".
  2243.     "you must get the JPEG library. Please see the PHP docs for details.");
  2244.     return true;
  2245. }
  2246. else {
  2247.     if( $aFormat=="jpeg" || $aFormat=="png" || $aFormat=="gif" ) {
  2248. if( $aFormat=="jpeg" && !($supported & IMG_JPG) )
  2249.     $tst=false;
  2250. elseif( $aFormat=="png" && !($supported & IMG_PNG) ) 
  2251.     $tst=false;
  2252. elseif( $aFormat=="gif" && !($supported & IMG_GIF) ) 
  2253.     $tst=false;
  2254. else {
  2255.     $this->img_format=$aFormat;
  2256.     return true;
  2257. }
  2258.     }
  2259.     else 
  2260. $tst=false;
  2261.     if( !$tst )
  2262. JpGraphError::Raise(" Your PHP installation does not support the chosen graphic format: $aFormat");
  2263. }
  2264.     }
  2265. } // CLASS
  2266. //===================================================
  2267. // CLASS RotImage
  2268. // Description: Exactly as Image but draws the image at
  2269. // a specified angle around a specified rotation point.
  2270. //===================================================
  2271. class RotImage extends Image {
  2272.     var $m=array();
  2273.     var $a=0;
  2274.     var $dx=0,$dy=0,$transx=0,$transy=0; 
  2275.     function RotImage($aWidth,$aHeight,$a=0,$aFormat=DEFAULT_GFORMAT) {
  2276. $this->Image($aWidth,$aHeight,$aFormat);
  2277. $this->dx=$this->left_margin+$this->plotwidth/2;
  2278. $this->dy=$this->top_margin+$this->plotheight/2;
  2279. $this->SetAngle($a);
  2280.     }
  2281.     function SetCenter($dx,$dy) {
  2282. $old_dx = $this->dx;
  2283. $old_dy = $this->dy;
  2284. $this->dx=$dx;
  2285. $this->dy=$dy;
  2286. $this->SetAngle($this->a);
  2287. return array($old_dx,$old_dy);
  2288.     }
  2289.     function SetTranslation($dx,$dy) {
  2290. $old = array($this->transx,$this->transy);
  2291. $this->transx = $dx;
  2292. $this->transy = $dy;
  2293. return $old;
  2294.     }
  2295.     function UpdateRotMatrice()  {
  2296. $a = $this->a;
  2297. $a *= M_PI/180;
  2298. $sa=sin($a); $ca=cos($a);
  2299. // Create the rotation matrix
  2300. $this->m[0][0] = $ca;
  2301. $this->m[0][1] = -$sa;
  2302. $this->m[0][2] = $this->dx*(1-$ca) + $sa*$this->dy ;
  2303. $this->m[1][0] = $sa;
  2304. $this->m[1][1] = $ca;
  2305. $this->m[1][2] = $this->dy*(1-$ca) - $sa*$this->dx ;
  2306.     }
  2307.     function SetAngle($a) {
  2308. $tmp = $this->a;
  2309. $this->a = $a;
  2310. $this->UpdateRotMatrice();
  2311. return $tmp;
  2312.     }
  2313.     function Circle($xc,$yc,$r) {
  2314. // Circle get's rotated through the Arc() call
  2315. // made in the parent class
  2316. parent::Circle($xc,$yc,$r);
  2317.     }
  2318.     function FilledCircle($xc,$yc,$r) {
  2319. // If we use GD1 then Image::FilledCircle will use a 
  2320. // call to Arc so it will get rotated through the Arc
  2321. // call.
  2322. if( $GLOBALS['gd2'] ) {
  2323.     list($xc,$yc) = $this->Rotate($xc,$yc);
  2324. }
  2325. parent::FilledCircle($xc,$yc,$r);
  2326.     }
  2327.     function Arc($xc,$yc,$w,$h,$s,$e) {
  2328. list($xc,$yc) = $this->Rotate($xc,$yc);
  2329. $s += $this->a;
  2330. $e += $this->a;
  2331. parent::Arc($xc,$yc,$w,$h,$s,$e);
  2332.     }
  2333.     function FilledArc($xc,$yc,$w,$h,$s,$e) {
  2334. list($xc,$yc) = $this->Rotate($xc,$yc);
  2335. $s += $this->a;
  2336. $e += $this->a;
  2337. parent::FilledArc($xc,$yc,$w,$h,$s,$e);
  2338.     }
  2339.     function SetMargin($lm,$rm,$tm,$bm) {
  2340. parent::SetMargin($lm,$rm,$tm,$bm);
  2341. $this->dx=$this->left_margin+$this->plotwidth/2;
  2342. $this->dy=$this->top_margin+$this->plotheight/2;
  2343. $this->UpdateRotMatrice();
  2344.     }
  2345.     function Rotate($x,$y) {
  2346. // Optimization. Ignore rotation if Angle==0 || ANgle==360
  2347. if( $this->a == 0 || $this->a == 360 ) {
  2348.     return array($x + $this->transx, $y + $this->transy );
  2349. }
  2350. else {
  2351.     $x1=round($this->m[0][0]*$x + $this->m[0][1]*$y,1) + $this->m[0][2] + $this->transx;
  2352.     $y1=round($this->m[1][0]*$x + $this->m[1][1]*$y,1) + $this->m[1][2] + $this->transy;
  2353.     return array($x1,$y1);
  2354. }
  2355.     }
  2356.     function ArrRotate($pnts) {
  2357. for($i=0; $i < count($pnts)-1; $i+=2)
  2358.     list($pnts[$i],$pnts[$i+1]) = $this->Rotate($pnts[$i],$pnts[$i+1]);
  2359. return $pnts;
  2360.     }
  2361.     function Line($x1,$y1,$x2,$y2) {
  2362. list($x1,$y1) = $this->Rotate($x1,$y1);
  2363. list($x2,$y2) = $this->Rotate($x2,$y2);
  2364. parent::Line($x1,$y1,$x2,$y2);
  2365.     }
  2366.     function Rectangle($x1,$y1,$x2,$y2) {
  2367. // Rectangle uses Line() so it will be rotated through that call
  2368. parent::Rectangle($x1,$y1,$x2,$y2);
  2369.     }
  2370.     function FilledRectangle($x1,$y1,$x2,$y2) {
  2371. if( $y1==$y2 || $x1==$x2 )
  2372.     $this->Line($x1,$y1,$x2,$y2);
  2373. else 
  2374.     $this->FilledPolygon(array($x1,$y1,$x2,$y1,$x2,$y2,$x1,$y2));
  2375.     }
  2376.     function Polygon($pnts,$closed=FALSE) {
  2377. //Polygon uses Line() so it will be rotated through that call
  2378. parent::Polygon($pnts,$closed);
  2379.     }
  2380.     function FilledPolygon($pnts) {
  2381. parent::FilledPolygon($this->ArrRotate($pnts));
  2382.     }
  2383.     function Point($x,$y) {
  2384. list($xp,$yp) = $this->Rotate($x,$y);
  2385. parent::Point($xp,$yp);
  2386.     }
  2387.     function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
  2388. list($xp,$yp) = $this->Rotate($x,$y);
  2389. parent::StrokeText($xp,$yp,$txt,$dir,$paragraph_align,$debug);
  2390.     }
  2391. }
  2392. //===================================================
  2393. // CLASS ImgStreamCache
  2394. // Description: Handle caching of graphs to files
  2395. //===================================================
  2396. class ImgStreamCache {
  2397.     var $cache_dir;
  2398.     var $img=null;
  2399.     var $timeout=0;  // Infinite timeout
  2400.     //---------------
  2401.     // CONSTRUCTOR
  2402.     function ImgStreamCache(&$aImg, $aCacheDir=CACHE_DIR) {
  2403. $this->img = &$aImg;
  2404. $this->cache_dir = $aCacheDir;
  2405.     }
  2406. //---------------
  2407. // PUBLIC METHODS
  2408.     // Specify a timeout (in minutes) for the file. If the file is older then the
  2409.     // timeout value it will be overwritten with a newer version.
  2410.     // If timeout is set to 0 this is the same as infinite large timeout and if
  2411.     // timeout is set to -1 this is the same as infinite small timeout
  2412.     function SetTimeout($aTimeout) {
  2413. $this->timeout=$aTimeout;
  2414.     }
  2415.     // Output image to browser and also write it to the cache
  2416.     function PutAndStream(&$aImage,$aCacheFileName,$aInline,$aStrokeFileName) {
  2417. // Some debugging code to brand the image with numbe of colors
  2418. // used
  2419. GLOBAL $gJpgBrandTiming;
  2420. if( $gJpgBrandTiming ) {
  2421.     global $tim;
  2422.     $t=$tim->Pop()/1000.0;
  2423.     $c=$aImage->SetColor("black");
  2424.     $t=sprintf(BRAND_TIME_FORMAT,round($t,3));
  2425.     imagestring($this->img->img,2,5,$this->img->height-20,$t,$c);
  2426. }
  2427. // Check if we should stroke the image to an arbitrary file
  2428. if( $aStrokeFileName!="" ) {
  2429.     if( $aStrokeFileName == "auto" )
  2430. $aStrokeFileName = GenImgName();
  2431.     if( file_exists($aStrokeFileName) ) {
  2432. // Delete the old file
  2433. if( !@unlink($aStrokeFileName) )
  2434.     JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
  2435.     }
  2436.     $aImage->Stream($aStrokeFileName);
  2437.     return;
  2438. }
  2439. if( $aCacheFileName != "" && USE_CACHE) {
  2440.     $aCacheFileName = $this->cache_dir . $aCacheFileName;
  2441.     if( file_exists($aCacheFileName) ) {
  2442. if( !$aInline ) {
  2443.     // If we are generating image off-line (just writing to the cache)
  2444.     // and the file exists and is still valid (no timeout)
  2445.     // then do nothing, just return.
  2446.     $diff=time()-filemtime($aCacheFileName);
  2447.     if( $diff < 0 )
  2448. JpGraphError::Raise(" Cached imagefile ($aCacheFileName) has file date in the future!!");
  2449.     if( $this->timeout>0 && ($diff <= $this->timeout*60) ) 
  2450. return;
  2451. }
  2452. if( !@unlink($aCacheFileName) )
  2453.     JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
  2454. $aImage->Stream($aCacheFileName);
  2455.     }
  2456.     else {
  2457. $this->MakeDirs(dirname($aCacheFileName));
  2458. if( !is_writeable(dirname($aCacheFileName)) ) {
  2459.     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.');
  2460. }
  2461. $aImage->Stream($aCacheFileName);
  2462.     }
  2463.     $res=true;
  2464.     // Set group to specified
  2465.     if( CACHE_FILE_GROUP != "" )
  2466. $res = @chgrp($aCacheFileName,CACHE_FILE_GROUP);
  2467.     if( CACHE_FILE_MOD != "" )
  2468. $res = @chmod($aCacheFileName,CACHE_FILE_MOD);
  2469.     if( !$res )
  2470. JpGraphError::Raise(" Can't set permission for cached image $aStrokeFileName. Permission problem?");
  2471.     $aImage->Destroy();
  2472.     if( $aInline ) {
  2473. if ($fh = @fopen($aCacheFileName, "rb") ) {
  2474.     $this->img->Headers();
  2475.     fpassthru($fh);
  2476.     return;
  2477. }
  2478. else
  2479.     JpGraphError::Raise(" Cant open file from cache [$aFile]"); 
  2480.     }
  2481. }
  2482. elseif( $aInline ) {
  2483.     $this->img->Headers();  
  2484.     $aImage->Stream();
  2485.     return;
  2486. }
  2487.     }
  2488.     // Check if a given image is in cache and in that case
  2489.     // pass it directly on to web browser. Return false if the
  2490.     // image file doesn't exist or exists but is to old
  2491.     function GetAndStream($aCacheFileName) {
  2492. $aCacheFileName = $this->cache_dir.$aCacheFileName;  
  2493. if ( USE_CACHE && file_exists($aCacheFileName) && $this->timeout>=0 ) {
  2494.     $diff=time()-filemtime($aCacheFileName);
  2495.     if( $this->timeout>0 && ($diff > $this->timeout*60) ) {
  2496. return false;
  2497.     }
  2498.     else {
  2499. if ($fh = @fopen($aCacheFileName, "rb")) {
  2500.     $this->img->Headers();
  2501.     fpassthru($fh);
  2502.     return true;
  2503. }
  2504. else
  2505.     JpGraphError::Raise(" Can't open cached image "$aCacheFileName" for reading.");
  2506.     }
  2507. return false;
  2508.     }
  2509.     //---------------
  2510.     // PRIVATE METHODS
  2511.     // Create all necessary directories in a path
  2512.     function MakeDirs($aFile) {
  2513. $dirs = array();
  2514. while ( !(file_exists($aFile)) ) {
  2515.     $dirs[] = $aFile;
  2516.     $aFile = dirname($aFile);
  2517. }
  2518. for ($i = sizeof($dirs)-1; $i>=0; $i--) {
  2519.     if(! @mkdir($dirs[$i],0777) )
  2520. JpGraphError::Raise(" Can't create directory $aFile. Make sure PHP has write permission to this directory.");
  2521.     // We also specify mode here after we have changed group. 
  2522.     // This is necessary if Apache user doesn't belong the
  2523.     // default group and hence can't specify group permission
  2524.     // in the previous mkdir() call
  2525.     if( CACHE_FILE_GROUP != "" ) {
  2526. $res=true;
  2527. $res =@chgrp($dirs[$i],CACHE_FILE_GROUP);
  2528. $res &= @chmod($dirs[$i],0777);
  2529. if( !$res )
  2530.     JpGraphError::Raise(" Can't set permissions for $aFile. Permission problems?");
  2531.     }
  2532. }
  2533. return true;
  2534.     }
  2535. } // CLASS Cache
  2536. //===================================================
  2537. // CLASS Legend
  2538. // Description: Responsible for drawing the box containing
  2539. // all the legend text for the graph
  2540. //===================================================
  2541. DEFINE('_DEFAULT_LPM_SIZE',8);
  2542. class Legend {
  2543.     var $color=array(0,0,0); // Default fram color
  2544.     var $fill_color=array(235,235,235); // Default fill color
  2545.     var $shadow=true; // Shadow around legend "box"
  2546.     var $shadow_color='gray';
  2547.     var $txtcol=array();
  2548.     var $mark_abs_size=_DEFAULT_LPM_SIZE;
  2549.     var $xmargin=10,$ymargin=6,$shadow_width=2;
  2550.     var $xpos=0.05, $ypos=0.15, $xabspos=-1, $yabspos=-1;
  2551. var $halign="right", $valign="top";
  2552.     var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
  2553.     var $font_color='black';
  2554.     var $hide=false,$layout_n=1;
  2555.     var $weight=1,$frameweight=1;
  2556.     var $csimareas='';
  2557. //---------------
  2558. // CONSTRUCTOR
  2559.     function Legend() {
  2560. // Empty
  2561.     }
  2562. //---------------
  2563. // PUBLIC METHODS
  2564.     function Hide($aHide=true) {
  2565. $this->hide=$aHide;
  2566.     }
  2567.     function SetShadow($aShow='gray',$aWidth=2) {
  2568. if( is_string($aShow) ) {
  2569.     $this->shadow_color = $aShow;
  2570.     $this->shadow=true;
  2571. }
  2572. else
  2573.     $this->shadow=$aShow;
  2574. $this->shadow_width=$aWidth;
  2575.     }
  2576.     function SetMarkAbsSize($aSize) {
  2577. $this->mark_abs_size = $aSize ;
  2578.     }
  2579.     function SetLineWeight($aWeight) {
  2580. $this->weight = $aWeight;
  2581.     }
  2582.     function SetFrameWeight($aWeight) {
  2583. $this->frameweight = $aWeight;
  2584.     }
  2585.     function SetLayout($aDirection=LEGEND_VERT) {
  2586. $this->layout_n = $aDirection==LEGEND_VERT ? 1 : 99 ;
  2587.     }
  2588.     function SetColumns($aCols) {
  2589. $this->layout_n = $aCols ;
  2590.     }
  2591.     function SetLineSpacing($aSpacing) {
  2592. $this->ymargin = $aSpacing ;
  2593.     }
  2594.     // Set color on frame around box
  2595.     function SetColor($aFontColor,$aColor='black') {
  2596. $this->font_color=$aFontColor;
  2597. $this->color=$aColor;
  2598.     }
  2599.     function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
  2600. $this->font_family = $aFamily;
  2601. $this->font_style = $aStyle;
  2602. $this->font_size = $aSize;
  2603.     }
  2604.     function SetPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
  2605. $this->Pos($aX,$aY,$aHAlign,$aVAlign);
  2606.     }
  2607.     function SetAbsPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
  2608. $this->xabspos=$aX;
  2609. $this->yabspos=$aY;
  2610. $this->halign=$aHAlign;
  2611. $this->valign=$aVAlign;
  2612.     }
  2613.     function Pos($aX,$aY,$aHAlign="right",$aVAlign="top") {
  2614. if( !($aX<1 && $aY<1) )
  2615.     JpGraphError::Raise(" Position for legend must be given as percentage in range 0-1");
  2616. $this->xpos=$aX;
  2617. $this->ypos=$aY;
  2618. $this->halign=$aHAlign;
  2619. $this->valign=$aVAlign;
  2620.     }
  2621.     function SetFillColor($aColor) {
  2622. $this->fill_color=$aColor;
  2623.     }
  2624.     function Add($aTxt,$aColor,$aPlotmark="",$aLinestyle=0,$csimtarget="",$csimalt="") {
  2625. $this->txtcol[]=array($aTxt,$aColor,$aPlotmark,$aLinestyle,$csimtarget,$csimalt);
  2626.     }
  2627.     function GetCSIMAreas() {
  2628. return $this->csimareas;
  2629.     }
  2630.     function Stroke(&$aImg) {
  2631. if( $this->hide ) return;
  2632. $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
  2633. $n=count($this->txtcol);
  2634. if( $n == 0 ) return;
  2635. // Find out the max width and height of each column to be able
  2636.         // to size the legend box.
  2637. $numcolumns = ($n > $this->layout_n ? $this->layout_n : $n);
  2638. for( $i=0; $i < $numcolumns; ++$i ) {
  2639.     $colwidth[$i] = $aImg->GetTextWidth($this->txtcol[$i][0]) +
  2640.             2*$this->xmargin + 2*$this->mark_abs_size;
  2641.     $colheight[$i] = 0;
  2642. }
  2643. // Find our maximum height in each row
  2644. $rows = -1 ;
  2645. for( $i=0; $i < $n; ++$i ) {
  2646.     $h = max($this->mark_abs_size,$aImg->GetTextHeight($this->txtcol[$i][0]))+$this->ymargin;
  2647.     if( $i % $numcolumns == 0 ) {
  2648. $rows++;
  2649. $rowheight[$rows] = 0;
  2650.     }
  2651.     $rowheight[$rows] = max($rowheight[$rows],$h);
  2652. }
  2653. $abs_height = 0;
  2654. for( $i=0; $i <= $rows; ++$i ) {
  2655.     $abs_height += $rowheight[$i] ;
  2656. }
  2657. // Make damn sure that the height is at least as high as mark size + ymargin
  2658. $abs_height = max($abs_height,$this->mark_abs_size+$this->ymargin);
  2659. $abs_height += 2*$this->ymargin;
  2660. // Find out the maximum width in each column
  2661. for( $i=$numcolumns; $i < $n; ++$i ) {
  2662.     $colwidth[$i % $numcolumns] = max(
  2663. $aImg->GetTextWidth($this->txtcol[$i][0])+2*$this->xmargin+2*$this->mark_abs_size, 
  2664. $colwidth[$i % $numcolumns]);
  2665. }
  2666. // Get the total width
  2667. $mtw = 0;
  2668. for( $i=0; $i < $numcolumns; ++$i ) {
  2669.     $mtw += $colwidth[$i];
  2670. }
  2671. // Find out maximum width we need for legend box
  2672. $abs_width = $mtw+$this->xmargin;
  2673. if( $this->xabspos === -1  && $this->yabspos === -1 ) {
  2674.     $this->xabspos = $this->xpos*$aImg->width ;
  2675.     $this->yabspos = $this->ypos*$aImg->height ;
  2676. }
  2677. // Positioning of the legend box
  2678. if( $this->halign=="left" )
  2679.     $xp = $this->xabspos; 
  2680. elseif( $this->halign=="center" )
  2681.     $xp = $this->xabspos - $abs_width/2; 
  2682. else  
  2683.     $xp = $aImg->width - $this->xabspos - $abs_width;
  2684. $yp=$this->yabspos;
  2685. if( $this->valign=="center" )
  2686.     $yp-=$abs_height/2;
  2687. elseif( $this->valign=="bottom" )
  2688.     $yp-=$abs_height;
  2689. // Stroke legend box
  2690. $aImg->SetColor($this->color);
  2691. $aImg->SetLineWeight($this->frameweight);
  2692. if( $this->shadow )
  2693.     $aImg->ShadowRectangle($xp,$yp,$xp+$abs_width+$this->shadow_width,
  2694.    $yp+$abs_height+$this->shadow_width,
  2695.    $this->fill_color,$this->shadow_width,$this->shadow_color);
  2696. else {
  2697.     $aImg->SetColor($this->fill_color);
  2698.     $aImg->FilledRectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
  2699.     $aImg->SetColor($this->color);
  2700.     $aImg->Rectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
  2701. }
  2702. // x1,y1 is the position for the legend mark
  2703. $aImg->SetLineWeight($this->weight);
  2704. $x1=$xp+$this->mark_abs_size/2+3;
  2705. $y1=$yp + $this->mark_abs_size/2 + $this->ymargin/2; 
  2706. $f2 =  round($aImg->GetTextHeight('X')/2);
  2707. $grad = new Gradient($aImg);
  2708. // Now stroke each legend in turn
  2709. $i = 1 ; $row = 0;
  2710. foreach($this->txtcol as $p) {
  2711.     $x1 = round($x1); $y1=round($y1);
  2712.     if ( $p[2] != "" && $p[2]->GetType() > -1 ) {
  2713. // Make a plot mark legend
  2714. $aImg->SetColor($p[1]);
  2715. if( $p[3] > 0 ) {
  2716.     $aImg->SetLineStyle($p[3]);
  2717.     $aImg->StyleLine($x1-3,$y1+$f2,$x1+$this->mark_abs_size+3,$y1+$f2);
  2718. }
  2719. // Stroke a mark with the standard size
  2720. // (As long as it is not an image mark )
  2721. if( $p[2]->GetType() != MARK_IMG ) {
  2722.     $p[2]->iFormatCallback = '';
  2723.     if( $this->mark_abs_size === _DEFAULT_LPM_SIZE )
  2724. $p[2]->SetDefaultWidth();  
  2725.     else 
  2726.         $p[2]->SetSize($this->mark_abs_size);
  2727.     $p[2]->Stroke($aImg,$x1+$this->mark_abs_size/2,$y1+$f2);
  2728. }
  2729.     } 
  2730.     elseif ( $p[2] != "" && (is_string($p[3]) || $p[3]>0 ) ) {
  2731. // Draw a styled line
  2732. $aImg->SetColor($p[1]);
  2733. $aImg->SetLineStyle($p[3]);
  2734. $aImg->StyleLine($x1,$y1+$f2,$x1+$this->mark_abs_size,$y1+$f2);
  2735. $aImg->StyleLine($x1,$y1+$f2+1,$x1+$this->mark_abs_size,$y1+$f2+1);
  2736.     } 
  2737.     else {
  2738. // Draw a colored box
  2739. $color = $p[1] ;
  2740. $ym =  round($y1 + $f2 - $this->mark_abs_size/2);
  2741. if( is_array($color) && count($color)==3 ) {
  2742.     // The client want a gradient color
  2743.     $grad->FilledRectangle($x1,$ym,
  2744.    $x1+$this->mark_abs_size,$ym+$this->mark_abs_size,
  2745.    $color[0],$color[1],$color[2]);
  2746. }
  2747. else {
  2748.     $aImg->SetColor($p[1]);
  2749.     $aImg->FilledRectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
  2750. }
  2751. $aImg->SetColor($this->color);
  2752. $aImg->SetLineWeight($this->weight);
  2753. $aImg->Rectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
  2754.     }
  2755.     $aImg->SetColor($this->font_color);
  2756.     $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
  2757.     $aImg->SetTextAlign("left","top");
  2758.     $aImg->StrokeText(round($x1+$this->mark_abs_size+$this->xmargin*1.5),$y1,$p[0]);
  2759.     // Add CSIM for Legend if defined
  2760.     if( $p[4] != "" ) {
  2761. $xe = $x1 + $this->xmargin+2*$this->mark_abs_size+$aImg->GetTextWidth($p[0]);
  2762. $ye = $y1 + max($this->mark_abs_size,$aImg->GetTextHeight($p[0]));
  2763. $coords = "$x1,$y1,$xe,$y1,$xe,$ye,$x1,$ye";
  2764. $this->csimareas .= "<area shape="poly" coords="$coords" href="".$p[4].""";
  2765. if( !empty($p[5]) ) {
  2766.     $tmp=sprintf($p[5],$p[0]);
  2767.     $this->csimareas .= " alt="$tmp" title="$tmp"";
  2768. }
  2769. $this->csimareas .= ">n";
  2770.     }
  2771.     if( $i >= $this->layout_n ) {
  2772. $x1 = $xp+$this->mark_abs_size/2+3;
  2773. //$y1 += max($aImg->GetTextHeight($p[0]),$this->mark_abs_size)+$this->ymargin;
  2774. $y1 += $rowheight[$row++];
  2775. $i = 1;
  2776.     }
  2777.     else {
  2778. $x1 += $colwidth[($i-1) % $numcolumns] ;
  2779. ++$i;
  2780.     }
  2781. }
  2782.     }
  2783. } // Class
  2784. //===================================================
  2785. // CLASS DisplayValue
  2786. // Description: Used to print data values at data points
  2787. //===================================================
  2788. class DisplayValue {    
  2789.     var $show=false,$format="%.1f",$negformat="";
  2790.     var $iFormCallback='';
  2791.     var $angle=0;
  2792.     var $ff=FF_FONT1,$fs=FS_NORMAL,$fsize=10;
  2793.     var $color="navy",$negcolor="";
  2794.     var $margin=5,$valign="",$halign="center";
  2795.     var $iHideZero=false;
  2796.     function Show($aFlag=true) {
  2797. $this->show=$aFlag;
  2798.     }
  2799.     function SetColor($aColor,$aNegcolor="") {
  2800. $this->color = $aColor;
  2801. $this->negcolor = $aNegcolor;
  2802.     }
  2803.     function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
  2804. $this->ff=$aFontFamily;
  2805. $this->fs=$aFontStyle;
  2806. $this->fsize=$aFontSize;
  2807.     }
  2808.     function SetMargin($aMargin) {
  2809. $this->margin = $aMargin;
  2810.     }
  2811.     function SetAngle($aAngle) {
  2812. $this->angle = $aAngle;
  2813.     }
  2814.     function SetAlign($aHAlign,$aVAlign='') {
  2815. $this->halign = $aHAlign;
  2816. $this->valign = $aVAlign;
  2817.     }
  2818.     function SetFormat($aFormat,$aNegFormat="") {
  2819. $this->format= $aFormat;
  2820. $this->negformat= $aNegFormat;
  2821.     }
  2822.     function SetFormatCallback($aFunc) {
  2823. $this->iFormCallback = $aFunc;
  2824.     }
  2825.     function HideZero($aFlag=true) {
  2826. $this->iHideZero=$aFlag;
  2827.     }
  2828.     function Stroke($img,$aVal,$x,$y) {
  2829. if( $this->show ) 
  2830. {
  2831.     if( $this->negformat=="" ) $this->negformat=$this->format;
  2832.     if( $this->negcolor=="" ) $this->negcolor=$this->color;
  2833.     if( $aVal===NULL || (is_string($aVal) && ($aVal=="" || $aVal=="-" || $aVal=="x" ) ) ) 
  2834. return;
  2835.     if( is_numeric($aVal) && $aVal==0 && $this->iHideZero ) {
  2836. return;
  2837.     }
  2838.     // Since the value is used in different cirumstances we need to check what
  2839.     // kind of formatting we shall use. For example, to display values in a line
  2840.     // graph we simply display the formatted value, but in the case where the user
  2841.     // has already specified a text string we don't fo anything.
  2842.     if( $this->iFormCallback != '' ) {
  2843. $f = $this->iFormCallback;
  2844. $sval = $f($aVal);
  2845.     }
  2846.     elseif( is_numeric($aVal) ) {
  2847. if( $aVal >= 0 )
  2848.     $sval=sprintf($this->format,$aVal);
  2849. else
  2850.     $sval=sprintf($this->negformat,$aVal);
  2851.     }
  2852.     else
  2853. $sval=$aVal;
  2854.     $y = $y-sign($aVal)*$this->margin;
  2855.     $txt = new Text($sval,$x,$y);
  2856.     $txt->SetFont($this->ff,$this->fs,$this->fsize);
  2857.     if( $this->valign == "" ) {
  2858. if( $aVal >= 0 )
  2859.     $valign = "bottom";
  2860. else
  2861.     $valign = "top";
  2862.     }
  2863.     else
  2864. $valign = $this->valign;
  2865.     $txt->Align($this->halign,$valign);
  2866.     $txt->SetOrientation($this->angle);
  2867.     if( $aVal > 0 )
  2868. $txt->SetColor($this->color);
  2869.     else
  2870. $txt->SetColor($this->negcolor);
  2871.     $txt->Stroke($img);
  2872. }
  2873.     }
  2874. }
  2875. //===================================================
  2876. // CLASS Plot
  2877. // Description: Abstract base class for all concrete plot classes
  2878. //===================================================
  2879. class Plot {
  2880.     var $line_weight=1;
  2881.     var $coords=array();
  2882.     var $legend='',$hidelegend=false;
  2883.     var $csimtargets=array(); // Array of targets for CSIM
  2884.     var $csimareas=""; // Resultant CSIM area tags
  2885.     var $csimalts=null; // ALT:s for corresponding target
  2886.     var $color="black";
  2887.     var $numpoints=0;
  2888.     var $weight=1;
  2889.     var $value;
  2890.     var $center=false;
  2891.     var $legendcsimtarget='';
  2892.     var $legendcsimalt='';
  2893. //---------------
  2894. // CONSTRUCTOR
  2895.     function Plot(&$aDatay,$aDatax=false) {
  2896. $this->numpoints = count($aDatay);
  2897. if( $this->numpoints==0 )
  2898.     JpGraphError::Raise(" Empty data array specified for plot. Must have at least one data point.");
  2899. $this->coords[0]=$aDatay;
  2900. if( is_array($aDatax) )
  2901.     $this->coords[1]=$aDatax;
  2902. $this->value = new DisplayValue();
  2903.     }
  2904. //---------------
  2905. // PUBLIC METHODS
  2906.     // Stroke the plot
  2907.     // "virtual" function which must be implemented by
  2908.     // the subclasses
  2909.     function Stroke(&$aImg,&$aXScale,&$aYScale) {
  2910. JpGraphError::Raise("JpGraph: Stroke() must be implemented by concrete subclass to class Plot");
  2911.     }
  2912.     function HideLegend($f=true) {
  2913. $this->hidelegend = $f;
  2914.     }
  2915.     function DoLegend(&$graph) {
  2916. if( !$this->hidelegend )
  2917.     $this->Legend($graph);
  2918.     }
  2919.     function StrokeDataValue($img,$aVal,$x,$y) {
  2920. $this->value->Stroke($img,$aVal,$x,$y);
  2921.     }
  2922.     // Set href targets for CSIM
  2923.     function SetCSIMTargets($aTargets,$aAlts=null) {
  2924. $this->csimtargets=$aTargets;
  2925. $this->csimalts=$aAlts;
  2926.     }
  2927.  
  2928.     // Get all created areas
  2929.     function GetCSIMareas() {
  2930. return $this->csimareas;
  2931.     }
  2932.     // "Virtual" function which gets called before any scale
  2933.     // or axis are stroked used to do any plot specific adjustment
  2934.     function PreStrokeAdjust(&$aGraph) {
  2935. if( substr($aGraph->axtype,0,4) == "text" && (isset($this->coords[1])) )
  2936.     JpGraphError::Raise("JpGraph: You can't use a text X-scale with specified X-coords. Use a "int" or "lin" scale instead.");
  2937. return true;
  2938.     }
  2939.     // Get minimum values in plot
  2940.     function Min() {
  2941. if( isset($this->coords[1]) )
  2942.     $x=$this->coords[1];
  2943. else
  2944.     $x="";
  2945. if( $x != "" && count($x) > 0 )
  2946.     $xm=min($x);
  2947. else 
  2948.     $xm=0;
  2949. $y=$this->coords[0];
  2950. if( count($y) > 0 ) {
  2951.     $ym = $y[0];
  2952.     $cnt = count($y);
  2953.     $i=0;
  2954.     while( $i<$cnt && !is_numeric($ym=$y[$i]) )
  2955. $i++;
  2956.     while( $i < $cnt) {
  2957. if( is_numeric($y[$i]) ) 
  2958.     $ym=min($ym,$y[$i]);
  2959. ++$i;
  2960.     }
  2961. }
  2962. else 
  2963.     $ym="";
  2964. return array($xm,$ym);
  2965.     }
  2966.     // Get maximum value in plot
  2967.     function Max() {
  2968. if( isset($this->coords[1]) )
  2969.     $x=$this->coords[1];
  2970. else
  2971.     $x="";
  2972. if( $x!="" && count($x) > 0 )
  2973.     $xm=max($x);
  2974. else {
  2975.     //$xm=count($this->coords[0])-1; // We count from 0..(n-1)
  2976.     $xm = $this->numpoints-1;
  2977. }
  2978. $y=$this->coords[0];
  2979. if( count($y) > 0 ) {
  2980.     if( !isset($y[0]) ) {
  2981. $y[0] = 0;
  2982. // Change in 1.5.1 Don't treat this as an error any more. Just silently concert to 0
  2983. // JpGraphError::Raise(" You have not specified a y[0] value!!");
  2984.     }
  2985.     $cnt = count($y);
  2986.     $i=0;
  2987.     while( $i<$cnt && !is_numeric($ym=$y[$i]) )
  2988. $i++;
  2989.     while( $i < $cnt ) {
  2990. if( is_numeric($y[$i]) ) $ym=max($ym,$y[$i]);
  2991. ++$i;
  2992.     }
  2993. }
  2994. else 
  2995.     $ym="";
  2996. return array($xm,$ym);
  2997.     }
  2998.     function SetColor($aColor) {
  2999. $this->color=$aColor;
  3000.     }
  3001.     function SetLegend($aLegend,$aCSIM="",$aCSIMAlt="") {
  3002. $this->legend = $aLegend;
  3003. $this->legendcsimtarget = $aCSIM;
  3004. $this->legendcsimalt = $aCSIMAlt;
  3005.     }
  3006.     function SetWeight($aWeight) {
  3007. $this->weight=$aWeight;
  3008.     }
  3009.     function SetLineWeight($aWeight=1) {
  3010. $this->line_weight=$aWeight;
  3011.     }
  3012.     function SetCenter($aCenter=true) {
  3013. $this->center = $aCenter;
  3014.     }
  3015.     // This method gets called by Graph class to plot anything that should go
  3016.     // into the margin after the margin color has been set.
  3017.     function StrokeMargin(&$aImg) {
  3018. return true;
  3019.     }
  3020.     // Framework function the chance for each plot class to set a legend
  3021.     function Legend(&$aGraph) {
  3022. if( $this->legend != "" )
  3023.     $aGraph->legend->Add($this->legend,$this->color,"",0,$this->legendcsimtarget,$this->legendcsimalt);    
  3024.     }
  3025. } // Class
  3026. require_once INCLUDE_PATH . "graph/jpgraph_plotmark.inc" ;
  3027. //==============================================================================
  3028. // The following section contains classes to implement the "band" functionality
  3029. //==============================================================================
  3030. // Utility class to hold coordinates for a rectangle
  3031. class Rectangle {
  3032.     var $x,$y,$w,$h;
  3033.     var $xe, $ye;
  3034.     function Rectangle($aX,$aY,$aWidth,$aHeight) {
  3035. $this->x=$aX;
  3036. $this->y=$aY;
  3037. $this->w=$aWidth;
  3038. $this->h=$aHeight;
  3039. $this->xe=$aX+$aWidth-1;
  3040. $this->ye=$aY+$aHeight-1;
  3041.     }
  3042. }
  3043. //=====================================================================
  3044. // Class RectPattern
  3045. // Base class for pattern hierarchi that is used to display patterned
  3046. // bands on the graph. Any subclass that doesn't override Stroke()
  3047. // must at least implement method DoPattern(&$aImg) which is responsible
  3048. // for drawing the pattern onto the graph.
  3049. //=====================================================================
  3050. class RectPattern {
  3051.     var $color;
  3052.     var $weight;
  3053.     var $rect=null;
  3054.     var $doframe=true;
  3055.     var $linespacing; // Line spacing in pixels
  3056.     var $iBackgroundColor=-1;  // Default is no background fill
  3057.     function RectPattern($aColor,$aWeight=1) {
  3058. $this->color = $aColor;
  3059. $this->weight = $aWeight;
  3060.     }
  3061.     function SetBackground($aBackgroundColor) {
  3062. $this->iBackgroundColor=$aBackgroundColor;
  3063.     }
  3064.     function SetPos(&$aRect) {
  3065. $this->rect = $aRect;
  3066.     }
  3067.     function ShowFrame($aShow=true) {
  3068. $this->doframe=$aShow;
  3069.     }
  3070.     function SetDensity($aDens) {
  3071. if( $aDens <1 || $aDens > 100 )
  3072.     JpGraphError::Raise(" Desity for pattern must be between 1 and 100. (You tried $aDens)");
  3073. // 1% corresponds to linespacing=50
  3074. // 100 % corresponds to linespacing 1
  3075. $this->linespacing = floor(((100-$aDens)/100.0)*50)+1;
  3076.     }
  3077.     function Stroke(&$aImg) {
  3078. if( $this->rect == null )
  3079.     JpGraphError::Raise(" No positions specified for pattern.");
  3080. if( !(is_numeric($this->iBackgroundColor) && $this->iBackgroundColor==-1) ) {
  3081.     $aImg->SetColor($this->iBackgroundColor);
  3082.     $aImg->FilledRectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye); 
  3083. }
  3084. $aImg->SetColor($this->color);
  3085. $aImg->SetLineWeight($this->weight);
  3086. // Virtual function implemented by subclass
  3087. $this->DoPattern($aImg);
  3088. // Frame around the pattern area
  3089. if( $this->doframe ) 
  3090.     $aImg->Rectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
  3091.     }
  3092. }
  3093. //=====================================================================
  3094. // Class RectPatternSolid
  3095. // Implements a solid band
  3096. //=====================================================================
  3097. class RectPatternSolid extends RectPattern {
  3098.     function RectPatternSolid($aColor="black",$aWeight=1) {
  3099. parent::RectPattern($aColor,$aWeight);
  3100.     }
  3101.     function DoPattern(&$aImg) {
  3102. $aImg->SetColor($this->color);
  3103. $aImg->FilledRectangle($this->rect->x,$this->rect->y,
  3104.        $this->rect->xe,$this->rect->ye);
  3105.     }
  3106. }
  3107. //=====================================================================
  3108. // Class RectPatternHor
  3109. // Implements horizontal line pattern
  3110. //=====================================================================
  3111. class RectPatternHor extends RectPattern {
  3112.     function RectPatternHor($aColor="black",$aWeight=1,$aLineSpacing=7) {
  3113. parent::RectPattern($aColor,$aWeight);
  3114. $this->linespacing = $aLineSpacing;
  3115.     }
  3116.     function DoPattern(&$aImg) {
  3117. $x0 = $this->rect->x;
  3118. $x1 = $this->rect->xe;
  3119. $y = $this->rect->y;
  3120. while( $y < $this->rect->ye ) {
  3121.     $aImg->Line($x0,$y,$x1,$y);
  3122.     $y += $this->linespacing;
  3123. }
  3124.     }
  3125. }
  3126. //=====================================================================
  3127. // Class RectPatternVert
  3128. // Implements vertical line pattern
  3129. //=====================================================================
  3130. class RectPatternVert extends RectPattern {
  3131.     var $linespacing=10; // Line spacing in pixels
  3132.     function RectPatternVert($aColor="black",$aWeight=1,$aLineSpacing=7) {
  3133. parent::RectPattern($aColor,$aWeight);
  3134. $this->linespacing = $aLineSpacing;
  3135.     }
  3136.     //--------------------
  3137.     // Private methods
  3138.     //
  3139.     function DoPattern(&$aImg) {
  3140. $x = $this->rect->x;
  3141. $y0 = $this->rect->y;
  3142. $y1 = $this->rect->ye;
  3143. while( $x < $this->rect->xe ) {
  3144.     $aImg->Line($x,$y0,$x,$y1);
  3145.     $x += $this->linespacing;
  3146. }
  3147.     }
  3148. }
  3149. //=====================================================================
  3150. // Class RectPatternRDiag
  3151. // Implements right diagonal pattern
  3152. //=====================================================================
  3153. class RectPatternRDiag extends RectPattern {
  3154.     var $linespacing; // Line spacing in pixels
  3155.     function RectPatternRDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
  3156. parent::RectPattern($aColor,$aWeight);
  3157. $this->linespacing = $aLineSpacing;
  3158.     }
  3159.     function DoPattern(&$aImg) {
  3160. //  --------------------
  3161. //  | /   /   /   /   /|
  3162. //  |/   /   /   /   / |
  3163. //  |   /   /   /   /  |
  3164. //  --------------------
  3165. $xe = $this->rect->xe;
  3166. $ye = $this->rect->ye;
  3167. $x0 = $this->rect->x + round($this->linespacing/2); 
  3168. $y0 = $this->rect->y;
  3169. $x1 = $this->rect->x; 
  3170. $y1 = $this->rect->y + round($this->linespacing/2);
  3171. while($x0<=$xe && $y1<=$ye) {
  3172.     $aImg->Line($x0,$y0,$x1,$y1);
  3173.     $x0 += $this->linespacing;
  3174.     $y1 += $this->linespacing;
  3175. }
  3176. if( $xe-$x1 > $ye-$y0 ) { 
  3177.     // Width larger than height
  3178.     $x1 = $this->rect->x + ($y1-$ye);
  3179.     $y1 = $ye; 
  3180.     $y0 = $this->rect->y; 
  3181.     while( $x0 <= $xe ) {
  3182. $aImg->Line($x0,$y0,$x1,$y1);
  3183. $x0 += $this->linespacing;
  3184. $x1 += $this->linespacing;
  3185.     }
  3186.     
  3187.     $y0=$this->rect->y + ($x0-$xe);
  3188.     $x0=$xe;
  3189. }
  3190. else {
  3191.     // Height larger than width
  3192.     $diff = $x0-$xe;
  3193.     $y0 = $diff+$this->rect->y;
  3194.     $x0 = $xe;
  3195.     $x1 = $this->rect->x;
  3196.     while( $y1 <= $ye ) {
  3197. $aImg->Line($x0,$y0,$x1,$y1);
  3198. $y1 += $this->linespacing;
  3199. $y0 += $this->linespacing;
  3200.     }
  3201.     
  3202.     $diff = $y1-$ye;
  3203.     $y1 = $ye;
  3204.     $x1 = $diff + $this->rect->x;
  3205. }
  3206. while( $y0 <= $ye ) {
  3207.     $aImg->Line($x0,$y0,$x1,$y1);
  3208.     $y0 += $this->linespacing;
  3209.     $x1 += $this->linespacing;
  3210. }
  3211.     }
  3212. }
  3213.  
  3214. //=====================================================================
  3215. // Class RectPatternLDiag
  3216. // Implements left diagonal pattern
  3217. //=====================================================================
  3218. class RectPatternLDiag extends RectPattern {
  3219.     var $linespacing; // Line spacing in pixels
  3220.     function RectPatternLDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
  3221. $this->linespacing = $aLineSpacing;
  3222. parent::RectPattern($aColor,$aWeight);
  3223.     }
  3224.     function DoPattern(&$aImg) {
  3225. //  --------------------
  3226. //  |             |
  3227. //  |             |
  3228. //  |              |
  3229. //  |------------------|
  3230. $xe = $this->rect->xe;
  3231. $ye = $this->rect->ye;
  3232. $x0 = $this->rect->x + round($this->linespacing/2); 
  3233. $y0 = $this->rect->ye;
  3234. $x1 = $this->rect->x; 
  3235. $y1 = $this->rect->ye - round($this->linespacing/2);
  3236. while($x0<=$xe && $y1>=$this->rect->y) {
  3237.     $aImg->Line($x0,$y0,$x1,$y1);
  3238.     $x0 += $this->linespacing;
  3239.     $y1 -= $this->linespacing;
  3240. }
  3241. if( $xe-$x1 > $ye-$this->rect->y ) { 
  3242.     // Width larger than height
  3243.     $x1 = $this->rect->x + ($this->rect->y-$y1);
  3244.     $y0=$ye; $y1=$this->rect->y; 
  3245.     while( $x0 <= $xe ) {
  3246. $aImg->Line($x0,$y0,$x1,$y1);
  3247. $x0 += $this->linespacing;
  3248. $x1 += $this->linespacing;
  3249.     }
  3250.     
  3251.     $y0=$this->rect->ye - ($x0-$xe);
  3252.     $x0=$xe;
  3253. }
  3254. else {
  3255.     // Height larger than width
  3256.     $diff = $x0-$xe;
  3257.     $y0 = $ye-$diff;
  3258.     $x0 = $xe;
  3259.     while( $y1 >= $this->rect->y ) {
  3260. $aImg->Line($x0,$y0,$x1,$y1);
  3261. $y0 -= $this->linespacing;
  3262. $y1 -= $this->linespacing;
  3263.     }     
  3264.     $diff = $this->rect->y - $y1;
  3265.     $x1 = $this->rect->x + $diff;
  3266.     $y1 = $this->rect->y;
  3267. }
  3268. while( $y0 >= $this->rect->y ) {
  3269.     $aImg->Line($x0,$y0,$x1,$y1);
  3270.     $y0 -= $this->linespacing;
  3271.     $x1 += $this->linespacing;
  3272. }
  3273.     }
  3274. }
  3275. //=====================================================================
  3276. // Class RectPattern3DPlane
  3277. // Implements "3D" plane pattern
  3278. //=====================================================================
  3279. class RectPattern3DPlane extends RectPattern {
  3280.     var $alpha=50;  // Parameter that specifies the distance
  3281.     // to "simulated" horizon in pixel from the
  3282.     // top of the band. Specifies how fast the lines
  3283.     // converge.
  3284.     function RectPattern3DPlane($aColor="black",$aWeight=1) {
  3285. parent::RectPattern($aColor,$aWeight);
  3286. $this->SetDensity(10);  // Slightly larger default
  3287.     }
  3288.     function SetHorizon($aHorizon) {
  3289. $this->alpha=$aHorizon;
  3290.     }
  3291.     function DoPattern(&$aImg) {
  3292. // "Fake" a nice 3D grid-effect. 
  3293. $x0 = $this->rect->x + $this->rect->w/2;
  3294. $y0 = $this->rect->y;
  3295. $x1 = $x0;
  3296. $y1 = $this->rect->ye;
  3297. $x0_right = $x0;
  3298. $x1_right = $x1;
  3299. // BTW "apa" means monkey in Swedish but is really a shortform for
  3300. // "alpha+a" which was the labels I used on paper when I derived the
  3301. // geometric to get the 3D perspective right. 
  3302. // $apa is the height of the bounding rectangle plus the distance to the
  3303. // artifical horizon (alpha)
  3304. $apa = $this->rect->h + $this->alpha;
  3305. // Three cases and three loops
  3306. // 1) The endpoint of the line ends on the bottom line
  3307. // 2) The endpoint ends on the side
  3308. // 3) Horizontal lines
  3309. // Endpoint falls on bottom line
  3310. $middle=$this->rect->x + $this->rect->w/2;
  3311. $dist=$this->linespacing;
  3312. $factor=$this->alpha /($apa);
  3313. while($x1>$this->rect->x) {
  3314.     $aImg->Line($x0,$y0,$x1,$y1);
  3315.     $aImg->Line($x0_right,$y0,$x1_right,$y1);
  3316.     $x1 = $middle - $dist;
  3317.     $x0 = $middle - $dist * $factor;
  3318.     $x1_right = $middle + $dist;
  3319.     $x0_right =  $middle + $dist * $factor;
  3320.     $dist += $this->linespacing;
  3321. }
  3322. // Endpoint falls on sides
  3323. $dist -= $this->linespacing;
  3324. $d=$this->rect->w/2;
  3325. $c = $apa - $d*$apa/$dist;
  3326. while( $x0>$this->rect->x ) {
  3327.     $aImg->Line($x0,$y0,$this->rect->x,$this->rect->ye-$c);
  3328.     $aImg->Line($x0_right,$y0,$this->rect->xe,$this->rect->ye-$c);
  3329.     $dist += $this->linespacing;
  3330.     $x0 = $middle - $dist * $factor;
  3331.     $x1 = $middle - $dist;
  3332.     $x0_right =  $middle + $dist * $factor;
  3333.     $c = $apa - $d*$apa/$dist;
  3334. }
  3335. // Horizontal lines
  3336. // They need some serious consideration since they are a function
  3337. // of perspective depth (alpha) and density (linespacing)
  3338. $x0=$this->rect->x;
  3339. $x1=$this->rect->xe;
  3340. $y=$this->rect->ye;
  3341. // The first line is drawn directly. Makes the loop below slightly
  3342. // more readable.
  3343. $aImg->Line($x0,$y,$x1,$y);
  3344. $hls = $this->linespacing;
  3345. // A correction factor for vertical "brick" line spacing to account for
  3346. // a) the difference in number of pixels hor vs vert
  3347. // b) visual apperance to make the first layer of "bricks" look more
  3348. // square.
  3349. $vls = $this->linespacing*0.6;
  3350. $ds = $hls*($apa-$vls)/$apa;
  3351. // Get the slope for the "perspective line" going from bottom right
  3352. // corner to top left corner of the "first" brick.
  3353. // Uncomment the following lines if you want to get a visual understanding
  3354. // of what this helpline does. BTW this mimics the way you would get the
  3355. // perspective right when drawing on paper.
  3356. /*
  3357.   $x0 = $middle;
  3358.   $y0 = $this->rect->ye;
  3359.   $len=floor(($this->rect->ye-$this->rect->y)/$vls);
  3360.   $x1 = $middle-round($len*$ds);
  3361.   $y1 = $this->rect->ye-$len*$vls;
  3362.   $aImg->PushColor("red");
  3363.   $aImg->Line($x0,$y0,$x1,$y1);
  3364.   $aImg->PopColor();
  3365. */
  3366. $y -= $vls;
  3367. $k=($this->rect->ye-($this->rect->ye-$vls))/($middle-($middle-$ds));
  3368. $dist = $hls;
  3369. while( $y>$this->rect->y ) {
  3370.     $aImg->Line($this->rect->x,$y,$this->rect->xe,$y);
  3371.     $adj = $k*$dist/(1+$dist*$k/$apa);
  3372.     if( $adj < 2 ) $adj=2;
  3373.     $y = $this->rect->ye - round($adj);
  3374.     $dist += $hls;
  3375. }
  3376.     }
  3377. }
  3378. //=====================================================================
  3379. // Class RectPatternCross
  3380. // Vert/Hor crosses
  3381. //=====================================================================
  3382. class RectPatternCross extends RectPattern {
  3383.     var $vert=null;
  3384.     var $hor=null;
  3385.     function RectPatternCross($aColor="black",$aWeight=1) {
  3386. parent::RectPattern($aColor,$aWeight);
  3387. $this->vert = new RectPatternVert($aColor,$aWeight);
  3388. $this->hor  = new RectPatternHor($aColor,$aWeight);
  3389.     }
  3390.     function SetOrder($aDepth) {
  3391. $this->vert->SetOrder($aDepth);
  3392. $this->hor->SetOrder($aDepth);
  3393.     }
  3394.     function SetPos(&$aRect) {
  3395. parent::SetPos($aRect);
  3396. $this->vert->SetPos($aRect);
  3397. $this->hor->SetPos($aRect);
  3398.     }
  3399.     function SetDensity($aDens) {
  3400. $this->vert->SetDensity($aDens);
  3401. $this->hor->SetDensity($aDens);
  3402.     }
  3403.     function DoPattern(&$aImg) {
  3404. $this->vert->DoPattern($aImg);
  3405. $this->hor->DoPattern($aImg);
  3406.     }
  3407. }
  3408. //=====================================================================
  3409. // Class RectPatternDiagCross
  3410. // Vert/Hor crosses
  3411. //=====================================================================
  3412. class RectPatternDiagCross extends RectPattern {
  3413.     var $left=null;
  3414.     var $right=null;
  3415.     function RectPatternDiagCross($aColor="black",$aWeight=1) {
  3416. parent::RectPattern($aColor,$aWeight);
  3417. $this->right = new RectPatternRDiag($aColor,$aWeight);
  3418. $this->left  = new RectPatternLDiag($aColor,$aWeight);
  3419.     }
  3420.     function SetOrder($aDepth) {
  3421. $this->left->SetOrder($aDepth);
  3422. $this->right->SetOrder($aDepth);
  3423.     }
  3424.     function SetPos(&$aRect) {
  3425. parent::SetPos($aRect);
  3426. $this->left->SetPos($aRect);
  3427. $this->right->SetPos($aRect);
  3428.     }
  3429.     function SetDensity($aDens) {
  3430. $this->left->SetDensity($aDens);
  3431. $this->right->SetDensity($aDens);
  3432.     }
  3433.     function DoPattern(&$aImg) {
  3434. $this->left->DoPattern($aImg);
  3435. $this->right->DoPattern($aImg);
  3436.     }
  3437. }
  3438. //=====================================================================
  3439. // Class RectPatternFactory
  3440. // Factory class for rectangular pattern 
  3441. //=====================================================================
  3442. class RectPatternFactory {
  3443.     function RectPatternFactory() {
  3444. // Empty
  3445.     }
  3446.     function Create($aPattern,$aColor,$aWeight=1) {
  3447. switch($aPattern) {
  3448.     case BAND_RDIAG:
  3449. $obj =  new RectPatternRDiag($aColor,$aWeight);
  3450. break;
  3451.     case BAND_LDIAG:
  3452. $obj =  new RectPatternLDiag($aColor,$aWeight);
  3453. break;
  3454.     case BAND_SOLID:
  3455. $obj =  new RectPatternSolid($aColor,$aWeight);
  3456. break;
  3457.     case BAND_VLINE:
  3458. $obj =  new RectPatternVert($aColor,$aWeight);
  3459. break;
  3460.     case BAND_HLINE:
  3461. $obj =  new RectPatternHor($aColor,$aWeight);
  3462. break;
  3463.     case BAND_3DPLANE:
  3464. $obj =  new RectPattern3DPlane($aColor,$aWeight);
  3465. break;
  3466.     case BAND_HVCROSS:
  3467. $obj =  new RectPatternCross($aColor,$aWeight);
  3468. break;
  3469.     case BAND_DIAGCROSS:
  3470. $obj =  new RectPatternDiagCross($aColor,$aWeight);
  3471. break;
  3472.     default:
  3473. JpGraphError::Raise(" Unknown pattern specification ($aPattern)");
  3474. }
  3475. return $obj;
  3476.     }
  3477. }
  3478. //=====================================================================
  3479. // Class PlotBand
  3480. // Factory class which is used by the client.
  3481. // It is reposnsible for factoring the corresponding pattern
  3482. // concrete class.
  3483. //=====================================================================
  3484. class PlotBand {
  3485.     var $prect=null;
  3486.     var $depth;
  3487.     var $dir, $min, $max;
  3488.     function PlotBand($aDir,$aPattern,$aMin,$aMax,$aColor="black",$aWeight=1,$aDepth=DEPTH_BACK) {
  3489. $f =  new RectPatternFactory();
  3490. $this->prect = $f->Create($aPattern,$aColor,$aWeight);
  3491. $this->dir = $aDir;
  3492. $this->min = $aMin;
  3493. $this->max = $aMax;
  3494. $this->depth=$aDepth;
  3495.     }
  3496.     // Set position. aRect contains absolute image coordinates
  3497.     function SetPos(&$aRect) {
  3498. assert( $this->prect != null ) ;
  3499. $this->prect->SetPos($aRect);
  3500.     }
  3501.     function ShowFrame($aFlag=true) {
  3502. $this->prect->ShowFrame($aFlag);
  3503.     }
  3504.     // Set z-order. In front of pplot or in the back
  3505.     function SetOrder($aDepth) {
  3506. $this->depth=$aDepth;
  3507.     }
  3508.     function SetDensity($aDens) {
  3509. $this->prect->SetDensity($aDens);
  3510.     }
  3511.     function GetDir() {
  3512. return $this->dir;
  3513.     }
  3514.     function GetMin() {
  3515. return $this->min;
  3516.     }
  3517.     function GetMax() {
  3518. return $this->max;
  3519.     }
  3520.     // Display band
  3521.     function Stroke(&$aImg,&$aXScale,&$aYScale) {
  3522. assert( $this->prect != null ) ;
  3523. if( $this->dir == HORIZONTAL ) {
  3524.     if( $this->min === 'min' ) $this->min = $aYScale->GetMinVal();
  3525.     if( $this->max === 'max' ) $this->max = $aYScale->GetMaxVal();
  3526.             // Only draw the bar if it actually appears in the range
  3527.             if ($this->min < $aYScale->GetMaxVal() && $this->max > $aYScale->GetMinVal()) {
  3528.     
  3529.     // Trucate to limit of axis
  3530.     $this->min = max($this->min, $aYScale->GetMinVal());
  3531.     $this->max = min($this->max, $aYScale->GetMaxVal());
  3532.     $x=$aXScale->scale_abs[0];
  3533.     $y=$aYScale->Translate($this->max);
  3534.     $width=$aXScale->scale_abs[1]-$aXScale->scale_abs[0]+1;
  3535.     $height=abs($y-$aYScale->Translate($this->min))+1;
  3536.     $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
  3537.     $this->prect->Stroke($aImg);
  3538.             }
  3539. }
  3540. else { // VERTICAL
  3541.     if( $this->min === 'min' ) $this->min = $aXScale->GetMinVal();
  3542.     if( $this->max === 'max' ) $this->max = $aXScale->GetMaxVal();
  3543.             
  3544.             // Only draw the bar if it actually appears in the range
  3545.     if ($this->min < $aXScale->GetMaxVal() && $this->max > $aXScale->GetMinVal()) {
  3546.     
  3547.     // Trucate to limit of axis
  3548.     $this->min = max($this->min, $aXScale->GetMinVal());
  3549.     $this->max = min($this->max, $aXScale->GetMaxVal());
  3550.     $y=$aYScale->scale_abs[1];
  3551.     $x=$aXScale->Translate($this->min);
  3552.     $height=abs($aYScale->scale_abs[1]-$aYScale->scale_abs[0]);
  3553.     $width=abs($x-$aXScale->Translate($this->max));
  3554.     $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
  3555.     $this->prect->Stroke($aImg);
  3556.             }
  3557. }
  3558.     }
  3559. }
  3560. //===================================================
  3561. // CLASS PlotLine
  3562. // Description: 
  3563. // Data container class to hold properties for a static
  3564. // line that is drawn directly in the plot area.
  3565. // Usefull to add static borders inside a plot to show
  3566. // for example set-values
  3567. //===================================================
  3568. class PlotLine {
  3569.     var $weight=1;
  3570.     var $color="black";
  3571.     var $direction=-1; 
  3572.     var $scaleposition;
  3573. //---------------
  3574. // CONSTRUCTOR
  3575.     function PlotLine($aDir=HORIZONTAL,$aPos=0,$aColor="black",$aWeight=1) {
  3576. $this->direction = $aDir;
  3577. $this->color=$aColor;
  3578. $this->weight=$aWeight;
  3579. $this->scaleposition=$aPos;
  3580.     }
  3581. //---------------
  3582. // PUBLIC METHODS
  3583.     function SetPosition($aScalePosition) {
  3584. $this->scaleposition=$aScalePosition;
  3585.     }
  3586.     function SetDirection($aDir) {
  3587. $this->direction = $aDir;
  3588.     }
  3589.     function SetColor($aColor) {
  3590. $this->color=$aColor;
  3591.     }
  3592.     function SetWeight($aWeight) {
  3593. $this->weight=$aWeight;
  3594.     }
  3595.     function Stroke(&$aImg,&$aXScale,&$aYScale) {
  3596. $aImg->SetColor($this->color);
  3597. $aImg->SetLineWeight($this->weight);
  3598. if( $this->direction == VERTICAL ) {
  3599.     $ymin_abs=$aYScale->Translate($aYScale->GetMinVal());
  3600.     $ymax_abs=$aYScale->Translate($aYScale->GetMaxVal());
  3601.     $xpos_abs=$aXScale->Translate($this->scaleposition);
  3602.     $aImg->Line($xpos_abs, $ymin_abs, $xpos_abs, $ymax_abs);
  3603. }
  3604. elseif( $this->direction == HORIZONTAL ) {
  3605.     $xmin_abs=$aXScale->Translate($aXScale->GetMinVal());
  3606.     $xmax_abs=$aXScale->Translate($aXScale->GetMaxVal());
  3607.     $ypos_abs=$aYScale->Translate($this->scaleposition);
  3608.     $aImg->Line($xmin_abs, $ypos_abs, $xmax_abs, $ypos_abs);
  3609. }
  3610. else
  3611.     JpGraphError::Raise(" Illegal direction for static line");
  3612.     }
  3613. }
  3614. // <EOF>
  3615. ?>