1<?php
2/*=======================================================================
3// File:	JPGRAPH_BAR.PHP
4// Description:	Bar plot extension for JpGraph
5// Created: 	2001-01-08
6// Author:	Johan Persson (johanp@aditus.nu)
7// Ver:		$Id: jpgraph_bar.php 8819 2005-02-08 11:35:07Z tokul $
8//
9// License:	This code is released under QPL
10// Copyright (C) 2001,2002 Johan Persson
11//========================================================================
12*/
13
14//===================================================
15// CLASS Gradient
16// Description: Handles gradient fills. This is to be
17// considered a "friend" class of Class Image.
18//===================================================
19class Gradient {
20    var $img=null;
21//---------------
22// CONSTRUCTOR
23    function Gradient(&$img) {
24	$this->img = $img;
25    }
26
27//---------------
28// PUBLIC METHODS
29    // Produce a gradient filled rectangle with a smooth transition between
30    // two colors.
31    // ($xl,$yt) 	Top left corner
32    // ($xr,$yb)	Bottom right
33    // $from_color	Starting color in gradient
34    // $to_color	End color in the gradient
35    // $style		Which way is the gradient oriented?
36    function FilledRectangle($xl,$yt,$xr,$yb,$from_color,$to_color,$style=1) {
37	switch( $style ) {
38	    case 1:  // HORIZONTAL
39		$steps = abs($xr-$xl);
40	    $delta = $xr>=$xl ? 1 : -1;
41	    $this->GetColArray($from_color,$to_color,$steps,$colors);
42	    for( $i=0, $x=$xl; $i<$steps; ++$i ) {
43		$this->img->current_color = $colors[$i];
44		$this->img->Line($x,$yt,$x,$yb);
45		$x += $delta;
46	    }
47	    break;
48
49	    case 2: // VERTICAL
50		$steps = abs($yb-$yt);
51	    $delta = $yb>=$yt ? 1 : -1;
52	    $this->GetColArray($from_color,$to_color,$steps,$colors);
53	    for($i=0,$y=$yt; $i<$steps; ++$i) {
54		$this->img->current_color = $colors[$i];
55		$this->img->Line($xl,$y,$xr,$y);
56		$y += $delta;
57	    }
58	    break;
59
60	    case 3: // VERTICAL FROM MIDDLE
61		$steps = abs($yb-$yt)/2;
62	    $delta = $yb>=$yt ? 1 : -1;
63	    $this->GetColArray($from_color,$to_color,$steps,$colors);
64	    for($y=$yt, $i=0; $i<$steps;  ++$i) {
65		$this->img->current_color = $colors[$i];
66		$this->img->Line($xl,$y,$xr,$y);
67		$y += $delta;
68	    }
69	    --$i;
70	    for($j=0; $j<$steps; ++$j, --$i) {
71		$this->img->current_color = $colors[$i];
72		$this->img->Line($xl,$y,$xr,$y);
73		$y += $delta;
74	    }
75	    $this->img->Line($xl,$y,$xr,$y);
76	    break;
77
78	    case 4: // HORIZONTAL FROM MIDDLE
79		$steps = abs($xr-$xl)/2;
80	    $delta = $xr>=$xl ? 1 : -1;
81	    $this->GetColArray($from_color,$to_color,$steps,$colors);
82	    for($x=$xl, $i=0; $i<$steps; ++$i) {
83		$this->img->current_color = $colors[$i];
84		$this->img->Line($x,$yb,$x,$yt);
85		$x += $delta;
86	    }
87	    --$i;
88	    for($j=0; $j<$steps; ++$j, --$i) {
89		$this->img->current_color = $colors[$i];
90		$this->img->Line($x,$yb,$x,$yt);
91		$x += $delta;
92	    }
93	    $this->img->Line($x,$yb,$x,$yt);
94	    break;
95
96	    case 6: // HORIZONTAL WIDER MIDDLE
97		$steps = abs($xr-$xl)/3;
98	    $delta = $xr>=$xl ? 1 : -1;
99	    $this->GetColArray($from_color,$to_color,$steps,$colors);
100	    for($x=$xl, $i=0; $i<$steps; ++$i) {
101		$this->img->current_color = $colors[$i];
102		$this->img->Line($x,$yb,$x,$yt);
103		$x += $delta;
104	    }
105	    --$i;
106	    $this->img->current_color = $colors[$i];
107	    for($j=0; $j< $steps; ++$j) {
108		$this->img->Line($x,$yb,$x,$yt);
109		$x += $delta;
110	    }
111
112	    for($j=0; $j<$steps; ++$j, --$i) {
113		$this->img->current_color = $colors[$i];
114		$this->img->Line($x,$yb,$x,$yt);
115		$x += $delta;
116	    }
117	    break;
118
119	    case 7: // VERTICAL WIDER MIDDLE
120		$steps = abs($yb-$yt)/3;
121	    $delta = $yb>=$yt? 1 : -1;
122	    $this->GetColArray($from_color,$to_color,$steps,$colors);
123	    for($y=$yt, $i=0; $i<$steps;  ++$i) {
124		$this->img->current_color = $colors[$i];
125		$this->img->Line($xl,$y,$xr,$y);
126		$y += $delta;
127	    }
128	    --$i;
129	    $this->img->current_color = $colors[$i];
130	    for($j=0; $j< $steps; ++$j) {
131		$this->img->Line($xl,$y,$xr,$y);
132		$y += $delta;
133	    }
134	    for($j=0; $j<$steps; ++$j, --$i) {
135		$this->img->current_color = $colors[$i];
136		$this->img->Line($xl,$y,$xr,$y);
137		$y += $delta;
138	    }
139	    break;
140
141	    case 5: // Rectangle
142		$steps = floor(min(($yb-$yt)+1,($xr-$xl)+1)/2);
143	    $this->GetColArray($from_color,$to_color,$steps,$colors);
144	    $dx = ($xr-$xl)/2;
145	    $dy = ($yb-$yt)/2;
146	    $x=$xl;$y=$yt;$x2=$xr;$y2=$yb;
147	    for($x=$xl, $i=0; $x<$xl+$dx && $y<$yt+$dy ; ++$x, ++$y, --$x2, --$y2, ++$i) {
148		assert($i<count($colors));
149		$this->img->current_color = $colors[$i];
150		$this->img->Rectangle($x,$y,$x2,$y2);
151	    }
152	    $this->img->Line($x,$y,$x2,$y2);
153	    break;
154
155	    default:
156		die("JpGraph Error: Unknown gradient style (=$style).");
157		break;
158	}
159    }
160
161//---------------
162// PRIVATE METHODS
163    // Add to the image color map the necessary colors to do the transition
164    // between the two colors using $numcolors intermediate colors
165    function GetColArray($from_color,$to_color,$arr_size,&$colors,$numcols=100) {
166	if( $arr_size==0 ) return;
167	// If color is give as text get it's corresponding r,g,b values
168	$from_color = $this->img->rgb->Color($from_color);
169	$to_color = $this->img->rgb->Color($to_color);
170
171	$rdelta=($to_color[0]-$from_color[0])/$numcols;
172	$gdelta=($to_color[1]-$from_color[1])/$numcols;
173	$bdelta=($to_color[2]-$from_color[2])/$numcols;
174	$stepspercolor	= $numcols/$arr_size;
175	$prevcolnum	= -1;
176	for ($i=0; $i<$arr_size; ++$i) {
177	    $colnum	= floor($stepspercolor*$i);
178	    if ( $colnum == $prevcolnum )
179		$colors[$i]	= $colidx;
180	    else {
181		$r = floor($from_color[0] + $colnum*$rdelta);
182		$g = floor($from_color[1] + $colnum*$gdelta);
183		$b = floor($from_color[2] + $colnum*$bdelta);
184		$colidx = $this->img->rgb->Allocate(sprintf("#%02x%02x%02x",$r,$g,$b));
185		$colors[$i]	= $colidx;
186	    }
187	    $prevcolnum = $colnum;
188	}
189    }
190} // Class
191
192
193//===================================================
194// CLASS BarPlot
195// Description: Main code to produce a bar plot
196//===================================================
197class BarPlot extends Plot {
198    var $width=0.4; // in percent of major ticks
199    var $abswidth=-1; // Width in absolute pixels
200    var $fill_color="lightblue"; // Default is to fill with light blue
201    var $ybase=0; // Bars start at 0
202    var $align="center";
203    var $grad=false,$grad_style=1;
204    var $grad_fromcolor=array(50,50,200),$grad_tocolor=array(255,255,255);
205    var $bar_shadow=false;
206    var $bar_shadow_color="black";
207    var $bar_shadow_hsize=3,$bar_shadow_vsize=3;
208    var $valuepos='top';
209
210//---------------
211// CONSTRUCTOR
212    function BarPlot(&$datay,$datax=false) {
213	$this->Plot($datay,$datax);
214	++$this->numpoints;
215    }
216
217//---------------
218// PUBLIC METHODS
219
220    // Set a drop shadow for the bar (or rather an "up-right" shadow)
221    function SetShadow($color="black",$hsize=3,$vsize=3) {
222	$this->bar_shadow=true;
223	$this->bar_shadow_color=$color;
224	$this->bar_shadow_vsize=$vsize;
225	$this->bar_shadow_hsize=$hsize;
226
227	// Adjust the value margin to compensate for shadow
228	$this->value->margin += $vsize;
229    }
230
231    // DEPRECATED use SetYBase instead
232    function SetYMin($aYStartValue) {
233	//die("JpGraph Error: Deprecated function SetYMin. Use SetYBase() instead.");
234	$this->ybase=$aYStartValue;
235    }
236
237    // Specify the base value for the bars
238    function SetYBase($aYStartValue) {
239	$this->ybase=$aYStartValue;
240    }
241
242    function Legend(&$graph) {
243	if( $this->fill_color && $this->legend!="" ) {
244	    if( is_array($this->fill_color) )
245		$graph->legend->Add($this->legend,$this->fill_color[0],"",0,$this->legendcsimtarget,$this->legendcsimalt);
246	    else
247		$graph->legend->Add($this->legend,$this->fill_color,"",0,$this->legendcsimtarget,$this->legendcsimalt);
248	}
249    }
250
251    // Gets called before any axis are stroked
252    function PreStrokeAdjust(&$graph) {
253	parent::PreStrokeAdjust($graph);
254
255	// If we are using a log Y-scale we want the base to be at the
256	// minimum Y-value unless the user have specifically set some other
257	// value than the default.
258	if( substr($graph->axtype,-3,3)=="log" && $this->ybase==0 )
259	    $this->ybase = $graph->yaxis->scale->GetMinVal();
260
261	// For a "text" X-axis scale we will adjust the
262	// display of the bars a little bit.
263	if( substr($graph->axtype,0,3)=="tex" ) {
264	    // Position the ticks between the bars
265	    $graph->xaxis->scale->ticks->SetXLabelOffset(0.5,0);
266
267	    // Center the bars
268	    if( $this->align == "center" )
269	    	$graph->SetTextScaleOff(0.5-$this->width/2);
270	    elseif( $this->align == "right" )
271	    	$graph->SetTextScaleOff(1-$this->width);
272	}
273	else {
274	    // We only set an absolute width for linear and int scale
275	    // for text scale the width will be set to a fraction of
276	    // the majstep width.
277	    if( $this->abswidth == -1 ) // Not set
278		// set width to a visuable sensible default
279		$this->abswidth = $graph->img->plotwidth/(2*count($this->coords[0]));
280	}
281    }
282
283    function Min() {
284	$m = parent::Min();
285	if( $m[1] >= $this->ybase )
286	    $m[1] = $this->ybase;
287	return $m;
288    }
289
290    function Max() {
291	$m = parent::Max();
292	if( $m[1] <= $this->ybase )
293	    $m[1] = $this->ybase;
294	return $m;
295    }
296
297    // Specify width as fractions of the major stepo size
298    function SetWidth($aFractionWidth) {
299	$this->width=$aFractionWidth;
300    }
301
302    // Specify width in absolute pixels. If specified this
303    // overrides SetWidth()
304    function SetAbsWidth($aWidth) {
305	$this->abswidth=$aWidth;
306    }
307
308    function SetAlign($aAlign) {
309	$this->align=$aAlign;
310    }
311
312    function SetNoFill() {
313	$this->grad = false;
314	$this->fill_color=false;
315    }
316
317    function SetFillColor($aColor) {
318	$this->fill_color=$aColor;
319    }
320
321    function SetFillGradient($from_color,$to_color,$style) {
322	$this->grad=true;
323	$this->grad_fromcolor=$from_color;
324	$this->grad_tocolor=$to_color;
325	$this->grad_style=$style;
326    }
327
328    function SetValuePos($aPos) {
329	$this->valuepos = $aPos;
330    }
331
332    function Stroke(&$img,&$xscale,&$yscale) {
333
334	$numpoints = count($this->coords[0]);
335	if( isset($this->coords[1]) ) {
336	    if( count($this->coords[1])!=$numpoints )
337		die("JpGraph Error: Number of X and Y points are not equal.<br>
338					Number of X-points:".count($this->coords[1])."<br>
339					Number of Y-points:$numpoints");
340	    else
341		$exist_x = true;
342	}
343	else
344	    $exist_x = false;
345
346
347	$numbars=count($this->coords[0]);
348
349	// Use GetMinVal() instead of scale[0] directly since in the case
350	// of log scale we get a correct value. Log scales will have negative
351	// values for values < 1 while still not representing negative numbers.
352	if( $yscale->GetMinVal() >= 0 )
353	    $zp=$yscale->scale_abs[0];
354	else {
355	    $zp=$yscale->Translate(0);
356	}
357
358	if( $this->abswidth > -1 )
359	    $abswidth=$this->abswidth;
360	else
361	    $abswidth=round($this->width*$xscale->scale_factor,0);
362
363	for($i=0; $i<$numbars; $i++) {
364	    if( $exist_x ) $x=$this->coords[1][$i];
365	    else $x=$i;
366
367	    $x=$xscale->Translate($x);
368
369 		//if($this->align=="center")
370		//$x -= $abswidth/2;
371	    //elseif($this->align=="right")
372		//$x -= $abswidth;
373
374		if( !$xscale->textscale ) {
375	    	if($this->align=="center")
376				$x -= $abswidth/2;
377		    elseif($this->align=="right")
378				$x -= $abswidth;
379		}
380
381	    $pts=array(
382		$x,$zp,
383		$x,$yscale->Translate($this->coords[0][$i]),
384		$x+$abswidth,$yscale->Translate($this->coords[0][$i]),
385		$x+$abswidth,$zp);
386	    if( $this->grad ) {
387		$grad = new Gradient($img);
388		$grad->FilledRectangle($pts[2],$pts[3],
389		$pts[6],$pts[7],
390		$this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
391	    }
392	    elseif( !empty($this->fill_color) ) {
393		if(is_array($this->fill_color)) {
394		    $img->PushColor($this->fill_color[$i % count($this->fill_color)]);
395		} else {
396		    $img->PushColor($this->fill_color);
397		}
398		$img->FilledPolygon($pts);
399		$img->PopColor();
400	    }
401
402	    // Remember value of this bar
403	    $val=$this->coords[0][$i];
404
405	    if( $this->bar_shadow && $val != 0 ) {
406		$ssh = $this->bar_shadow_hsize;
407		$ssv = $this->bar_shadow_vsize;
408				// Create points to create a "upper-right" shadow
409		if( $val > 0 ) {
410		    $sp[0]=$pts[6];		$sp[1]=$pts[7];
411		    $sp[2]=$pts[4];		$sp[3]=$pts[5];
412		    $sp[4]=$pts[2];		$sp[5]=$pts[3];
413		    $sp[6]=$pts[2]+$ssh;	$sp[7]=$pts[3]-$ssv;
414		    $sp[8]=$pts[4]+$ssh;	$sp[9]=$pts[5]-$ssv;
415		    $sp[10]=$pts[6]+$ssh;	$sp[11]=$pts[7]-$ssv;
416		}
417		elseif( $val < 0 ) {
418		    $sp[0]=$pts[4];		$sp[1]=$pts[5];
419		    $sp[2]=$pts[6];		$sp[3]=$pts[7];
420		    $sp[4]=$pts[0];	$sp[5]=$pts[1];
421		    $sp[6]=$pts[0]+$ssh;	$sp[7]=$pts[1]-$ssv;
422		    $sp[8]=$pts[6]+$ssh;	$sp[9]=$pts[7]-$ssv;
423		    $sp[10]=$pts[4]+$ssh;	$sp[11]=$pts[5]-$ssv;
424		}
425
426		$img->PushColor($this->bar_shadow_color);
427		$img->FilledPolygon($sp);
428		$img->PopColor();
429	    }
430
431	    // Stroke the outline of the bar
432	    if( is_array($this->color) )
433		$img->SetColor($this->color[$i % count($this->color)]);
434	    else
435		$img->SetColor($this->color);
436	    $img->SetLineWeight($this->weight);
437	    $pts[] = $pts[0];
438	    $pts[] = $pts[1];
439	    $img->Polygon($pts);
440
441	    $x=$pts[2]+($pts[4]-$pts[2])/2;
442	    if( $this->valuepos=='top' ) {
443		$y=$pts[3];
444		$this->value->Stroke($img,$val,$x,$y);
445	    }
446	    elseif( $this->valuepos=='center' ) {
447		$y = ($pts[3] + $pts[1])/2;
448		$this->value->SetAlign('center','center');
449		$this->value->SetMargin(0);
450		$this->value->Stroke($img,$val,$x,$y);
451	    }
452	    elseif( $this->valuepos=='bottom' ) {
453		$y=$pts[1];
454		$this->value->SetMargin(0);
455		$this->value->Stroke($img,$val,$x,$y);
456	    }
457	    else {
458		JpGraphError::Raise('Unknown position for values on bars :'.$this->valuepos);
459		die();
460	    }
461	    // Create the client side image map
462	    $rpts = $img->ArrRotate($pts);
463	    $csimcoord=round($rpts[0]).", ".round($rpts[1]);
464	    for( $j=1; $j < 4; ++$j){
465		$csimcoord .= ", ".round($rpts[2*$j]).", ".round($rpts[2*$j+1]);
466	    }
467	    $this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
468	    if( !empty($this->csimtargets[$i]) )
469		$this->csimareas .= " href=\"".$this->csimtargets[$i]."\"";
470	    if( !empty($this->csimalts[$i]) ) {
471		$sval=sprintf($this->csimalts[$i],$this->coords[0][$i]);
472		$this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
473	    }
474	    $this->csimareas .= ">\n";
475	}
476	return true;
477    }
478} // Class
479
480//===================================================
481// CLASS GroupBarPlot
482// Description: Produce grouped bar plots
483//===================================================
484class GroupBarPlot extends BarPlot {
485    var $plots;
486    var $width=0.7;
487    var $nbrplots=0;
488    var $numpoints;
489//---------------
490// CONSTRUCTOR
491    function GroupBarPlot($plots) {
492	$this->plots = $plots;
493	$this->nbrplots = count($plots);
494	$this->numpoints = $plots[0]->numpoints;
495    }
496
497//---------------
498// PUBLIC METHODS
499    function Legend(&$graph) {
500	$n = count($this->plots);
501	for($i=0; $i<$n; ++$i)
502	    $this->plots[$i]->Legend($graph);
503    }
504
505    function Min() {
506	list($xmin,$ymin) = $this->plots[0]->Min();
507	$n = count($this->plots);
508	for($i=0; $i<$n; ++$i) {
509	    list($xm,$ym) = $this->plots[$i]->Min();
510	    $xmin = max($xmin,$xm);
511	    $ymin = min($ymin,$ym);
512	}
513	return array($xmin,$ymin);
514    }
515
516    function Max() {
517	list($xmax,$ymax) = $this->plots[0]->Max();
518	$n = count($this->plots);
519	for($i=0; $i<$n; ++$i) {
520	    list($xm,$ym) = $this->plots[$i]->Max();
521	    $xmax = max($xmax,$xm);
522	    $ymax = max($ymax,$ym);
523	}
524	return array($xmax,$ymax);
525    }
526
527    function GetCSIMareas() {
528	$n = count($this->plots);
529	$csimareas='';
530	for($i=0; $i < $n; ++$i) {
531	    $csimareas .= $this->plots[$i]->csimareas;
532	}
533	return $csimareas;
534    }
535
536    // Stroke all the bars next to each other
537    function Stroke(&$img,&$xscale,&$yscale) {
538	$tmp=$xscale->off;
539	$n = count($this->plots);
540	for( $i=0; $i<$n; ++$i ) {
541	    $this->plots[$i]->ymin=$this->ybase;
542	    $this->plots[$i]->SetWidth($this->width/$this->nbrplots);
543	    $xscale->off = $tmp+$i*round($xscale->ticks->major_step*$xscale->scale_factor*$this->width/$this->nbrplots);
544	    $this->plots[$i]->Stroke($img,$xscale,$yscale);
545	}
546	$xscale->off=$tmp;
547    }
548} // Class
549
550//===================================================
551// CLASS AccBarPlot
552// Description: Produce accumulated bar plots
553//===================================================
554class AccBarPlot extends BarPlot {
555    var $plots=null,$nbrplots=0,$numpoints=0;
556//---------------
557// CONSTRUCTOR
558    function AccBarPlot($plots) {
559	$this->plots = $plots;
560	$this->nbrplots = count($plots);
561	$this->numpoints = $plots[0]->numpoints;
562	$this->value = new DisplayValue();
563    }
564
565//---------------
566// PUBLIC METHODS
567    function Legend(&$graph) {
568	$n = count($this->plots);
569	for( $i=0; $i<$n; ++$i )
570	    $this->plots[$i]->Legend($graph);
571    }
572
573    function Max() {
574	list($xmax) = $this->plots[0]->Max();
575	$nmax=0;
576	for($i=0; $i<count($this->plots); ++$i) {
577	    $n = count($this->plots[$i]->coords[0]);
578	    $nmax = max($nmax,$n);
579	    list($x) = $this->plots[$i]->Max();
580	    $xmax = Max($xmax,$x);
581	}
582	for( $i = 0; $i < $nmax; $i++ ) {
583	    // Get y-value for bar $i by adding the
584	    // individual bars from all the plots added.
585	    // It would be wrong to just add the
586	    // individual plots max y-value since that
587	    // would in most cases give to large y-value.
588	    $y=$this->plots[0]->coords[0][$i];
589	    for( $j = 1; $j < $this->nbrplots; $j++ ) {
590		$y += $this->plots[ $j ]->coords[0][$i];
591	    }
592	    $ymax[$i] = $y;
593	}
594	$ymax = max($ymax);
595
596	// Bar always start at baseline
597	if( $ymax <= $this->ybase )
598	    $ymax = $this->ybase;
599	return array($xmax,$ymax);
600    }
601
602    function Min() {
603	$nmax=0;
604	list($xmin,$ysetmin) = $this->plots[0]->Min();
605	for($i=0; $i<count($this->plots); ++$i) {
606	    $n = count($this->plots[$i]->coords[0]);
607	    $nmax = max($nmax,$n);
608	    list($x,$y) = $this->plots[$i]->Min();
609	    $xmin = Min($xmin,$x);
610	    $ysetmin = Min($y,$ysetmin);
611	}
612	for( $i = 0; $i < $nmax; $i++ ) {
613	    // Get y-value for bar $i by adding the
614	    // individual bars from all the plots added.
615	    // It would be wrong to just add the
616	    // individual plots max y-value since that
617	    // would in most cases give to large y-value.
618	    $y=$this->plots[0]->coords[0][$i];
619	    for( $j = 1; $j < $this->nbrplots; $j++ ) {
620		$y += $this->plots[ $j ]->coords[0][$i];
621	    }
622	    $ymin[$i] = $y;
623	}
624	$ymin = Min($ysetmin,Min($ymin));
625	// Bar always start at baseline
626	if( $ymin >= $this->ybase )
627	    $ymin = $this->ybase;
628	return array($xmin,$ymin);
629    }
630
631    // Stroke acc bar plot
632    function Stroke(&$img,&$xscale,&$yscale) {
633	$img->SetLineWeight($this->weight);
634	for($i=0; $i<$this->numpoints-1; $i++) {
635	    $accy = 0;
636	    $accy_neg = 0;
637	    for($j=0; $j<$this->nbrplots; ++$j ) {
638		$img->SetColor($this->plots[$j]->color);
639
640		if ($this->plots[$j]->coords[0][$i] > 0) {
641		    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
642		    $accyt=$yscale->Translate($accy);
643		    $accy+=$this->plots[$j]->coords[0][$i];
644		} else {
645		    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
646		    $accyt=$yscale->Translate($accy_neg);
647		    $accy_neg+=$this->plots[$j]->coords[0][$i];
648		}
649
650		$xt=$xscale->Translate($i);
651
652		//echo "$i => $xt<br>";
653
654		if( $this->abswidth > -1 )
655		    $abswidth=$this->abswidth;
656		else
657		    $abswidth=round($this->width*$xscale->scale_factor,0);
658
659		$pts=array($xt,$accyt,$xt,$yt,$xt+$abswidth,$yt,$xt+$abswidth,$accyt);
660
661		if( $this->plots[$j]->grad ) {
662		    $grad = new Gradient($img);
663		    $grad->FilledRectangle(
664			$pts[2],$pts[3],
665			$pts[6],$pts[7],
666			$this->plots[$j]->grad_fromcolor,
667			$this->plots[$j]->grad_tocolor,
668			$this->plots[$j]->grad_style);
669		} elseif ($this->plots[$j]->fill_color ) {
670		    $img->SetColor($this->plots[$j]->fill_color);
671		    $img->FilledPolygon($pts);
672		    $img->SetColor($this->plots[$j]->color);
673		}
674
675		if( $this->bar_shadow ) {
676		    $ssh = $this->bar_shadow_hsize;
677		    $ssv = $this->bar_shadow_vsize;
678		    // Create points to create a "upper-right" shadow
679		    $sp[0]=$pts[6];		$sp[1]=$pts[7];
680		    $sp[2]=$pts[4];		$sp[3]=$pts[5];
681		    $sp[4]=$pts[2];	$sp[5]=$pts[3];
682		    $sp[6]=$pts[2]+$ssh;	$sp[7]=$pts[3]-$ssv;
683		    $sp[8]=$pts[4]+$ssh;	$sp[9]=$pts[5]-$ssv;
684		    $sp[10]=$pts[6]+$ssh;	$sp[11]=$pts[7]-$ssv;
685		    $img->SetColor($this->bar_shadow_color);
686		    $img->FilledPolygon($sp,4);
687		}
688
689		if( $i < count($this->plots[$j]->csimtargets) ) {
690		    // Create the client side image map
691		    $rpts = $img->ArrRotate($pts);
692		    $csimcoord=round($rpts[0]).", ".round($rpts[1]);
693		    for( $k=1; $k < 4; ++$k){
694			$csimcoord .= ", ".round($rpts[2*$k]).", ".round($rpts[2*$k+1]);
695		    }
696		    $this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
697		    $this->csimareas.= " href=\"".$this->plots[$j]->csimtargets[$i]."\"";
698		    if( !empty($this->plots[$j]->csimalts[$i]) ) {
699			$sval=sprintf($this->plots[$j]->csimalts[$i],$this->plots[$j]->coords[0][$i]);
700			$this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
701		    }
702		    $this->csimareas .= ">\n";
703		}
704		$pts[] = $pts[0];
705		$pts[] = $pts[1];
706		$img->Polygon($pts);
707	    }
708
709
710	    $x=$pts[2]+($pts[4]-$pts[2])/2;
711	    $y=$yscale->Translate($accy);
712	    if($this->bar_shadow) $x += $ssh;
713	    $this->value->Stroke($img,$accy,$x,$y);
714
715	    $accy = 0;
716	    $accy_neg = 0;
717	    for($j=0; $j<$this->nbrplots; ++$j ) {
718		if ($this->plots[$j]->coords[0][$i] > 0) {
719		    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
720		    $accyt=$yscale->Translate($accy);
721		    $y = $accyt-($accyt-$yt)/2;
722		    $accy+=$this->plots[$j]->coords[0][$i];
723		} else {
724		    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
725		    $accyt=$yscale->Translate($accy_neg);
726		    $y=0;
727		    $accy_neg+=$this->plots[$j]->coords[0][$i];
728		}
729		$this->plots[$j]->value->SetAlign("center","center");
730		$this->plots[$j]->value->SetMargin(0);
731		$this->plots[$j]->value->Stroke($img,$this->plots[$j]->coords[0][$i],$x,$y);
732	    }
733
734	}
735	return true;
736    }
737} // Class
738
739/* EOF */
740?>
741