Merge branch '138-toggle-free-look-with-hotkey' into 'main/atys-live'
[ryzomcore.git] / web / public_php / admin / jpgraph / jpgraph_line.php
blob60ef128ce6586ffb2dd0be3855b69a11a20ca21d
1 <?php
2 /*=======================================================================
3 // File: JPGRAPH_LINE.PHP
4 // Description: Line plot extension for JpGraph
5 // Created: 2001-01-08
6 // Author: Johan Persson (johanp@aditus.nu)
7 // Ver: $Id: jpgraph_line.php,v 1.1 2006/07/07 13:37:14 powles Exp $
8 //
9 // Copyright (c) Aditus Consulting. All rights reserved.
10 //========================================================================
13 require_once ('jpgraph_plotmark.inc');
15 // constants for the (filled) area
16 DEFINE("LP_AREA_FILLED", true);
17 DEFINE("LP_AREA_NOT_FILLED", false);
18 DEFINE("LP_AREA_BORDER",false);
19 DEFINE("LP_AREA_NO_BORDER",true);
21 //===================================================
22 // CLASS LinePlot
23 // Description:
24 //===================================================
25 class LinePlot extends Plot{
26 var $filled=false;
27 var $fill_color='blue';
28 var $mark=null;
29 var $step_style=false, $center=false;
30 var $line_style=1; // Default to solid
31 var $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
32 var $barcenter=false; // When we mix line and bar. Should we center the line in the bar.
33 var $fillFromMin = false ;
34 var $fillgrad=false,$fillgrad_fromcolor='navy',$fillgrad_tocolor='silver',$fillgrad_numcolors=100;
35 var $iFastStroke=false;
37 //---------------
38 // CONSTRUCTOR
39 function __construct(&$datay,$datax=false) {
40 parent::__construct($datay,$datax);
41 $this->mark = new PlotMark();
43 //---------------
44 // PUBLIC METHODS
46 // Set style, filled or open
47 function SetFilled($aFlag=true) {
48 JpGraphError::RaiseL(10001);//('LinePlot::SetFilled() is deprecated. Use SetFillColor()');
51 function SetBarCenter($aFlag=true) {
52 $this->barcenter=$aFlag;
55 function SetStyle($aStyle) {
56 $this->line_style=$aStyle;
59 function SetStepStyle($aFlag=true) {
60 $this->step_style = $aFlag;
63 function SetColor($aColor) {
64 parent::SetColor($aColor);
67 function SetFillFromYMin($f=true) {
68 $this->fillFromMin = $f ;
71 function SetFillColor($aColor,$aFilled=true) {
72 $this->fill_color=$aColor;
73 $this->filled=$aFilled;
76 function SetFillGradient($aFromColor,$aToColor,$aNumColors=100,$aFilled=true) {
77 $this->fillgrad_fromcolor = $aFromColor;
78 $this->fillgrad_tocolor = $aToColor;
79 $this->fillgrad_numcolors = $aNumColors;
80 $this->filled = $aFilled;
81 $this->fillgrad = true;
84 function Legend(&$graph) {
85 if( $this->legend!="" ) {
86 if( $this->filled && !$this->fillgrad ) {
87 $graph->legend->Add($this->legend,
88 $this->fill_color,$this->mark,0,
89 $this->legendcsimtarget,$this->legendcsimalt);
91 elseif( $this->fillgrad ) {
92 $color=array($this->fillgrad_fromcolor,$this->fillgrad_tocolor);
93 // In order to differentiate between gradients and cooors specified as an RGB triple
94 $graph->legend->Add($this->legend,$color,"",-2 /* -GRAD_HOR */,
95 $this->legendcsimtarget,$this->legendcsimalt);
97 else {
98 $graph->legend->Add($this->legend,
99 $this->color,$this->mark,$this->line_style,
100 $this->legendcsimtarget,$this->legendcsimalt);
105 function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
106 if($aMin > $aMax) {
107 // swap
108 $tmp = $aMin;
109 $aMin = $aMax;
110 $aMax = $tmp;
112 $this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
115 // Gets called before any axis are stroked
116 function PreStrokeAdjust(&$graph) {
118 // If another plot type have already adjusted the
119 // offset we don't touch it.
120 // (We check for empty in case the scale is a log scale
121 // and hence doesn't contain any xlabel_offset)
122 if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
123 $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
124 if( $this->center ) {
125 ++$this->numpoints;
126 $a=0.5; $b=0.5;
127 } else {
128 $a=0; $b=0;
130 $graph->xaxis->scale->ticks->SetXLabelOffset($a);
131 $graph->SetTextScaleOff($b);
132 //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
136 function SetFastStroke($aFlg=true) {
137 $this->iFastStroke = $aFlg;
140 function FastStroke(&$img,&$xscale,&$yscale,$aStartPoint=0,$exist_x=true) {
141 // An optimized stroke for many data points with no extra
142 // features but 60% faster. You can't have values or line styles, or null
143 // values in plots.
144 $numpoints=count($this->coords[0]);
145 if( $this->barcenter )
146 $textadj = 0.5-$xscale->text_scale_off;
147 else
148 $textadj = 0;
150 $img->SetColor($this->color);
151 $img->SetLineWeight($this->weight);
152 $pnts=$aStartPoint;
153 while( $pnts < $numpoints ) {
154 if( $exist_x ) $x=$this->coords[1][$pnts];
155 else $x=$pnts+$textadj;
156 $xt = $xscale->Translate($x);
157 $y=$this->coords[0][$pnts];
158 $yt = $yscale->Translate($y);
159 if( is_numeric($y) ) {
160 $cord[] = $xt;
161 $cord[] = $yt;
163 elseif( $y == '-' && $pnts > 0 ) {
164 // Just ignore
166 else {
167 JpGraphError::RaiseL(10002);//('Plot too complicated for fast line Stroke. Use standard Stroke()');
168 return;
170 ++$pnts;
171 } // WHILE
173 $img->Polygon($cord,false,true);
177 function Stroke(&$img,&$xscale,&$yscale) {
178 $idx=0;
179 $numpoints=count($this->coords[0]);
180 if( isset($this->coords[1]) ) {
181 if( count($this->coords[1])!=$numpoints )
182 JpGraphError::RaiseL(2003,count($this->coords[1]),$numpoints);
183 //("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
184 else
185 $exist_x = true;
187 else
188 $exist_x = false;
190 if( $this->barcenter )
191 $textadj = 0.5-$xscale->text_scale_off;
192 else
193 $textadj = 0;
195 // Find the first numeric data point
196 $startpoint=0;
197 while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) )
198 ++$startpoint;
200 // Bail out if no data points
201 if( $startpoint == $numpoints )
202 return;
204 if( $this->iFastStroke ) {
205 $this->FastStroke($img,$xscale,$yscale,$startpoint,$exist_x);
206 return;
209 if( $exist_x )
210 $xs=$this->coords[1][$startpoint];
211 else
212 $xs= $textadj+$startpoint;
214 $img->SetStartPoint($xscale->Translate($xs),
215 $yscale->Translate($this->coords[0][$startpoint]));
217 if( $this->filled ) {
218 $min = $yscale->GetMinVal();
219 if( $min > 0 || $this->fillFromMin )
220 $fillmin = $yscale->scale_abs[0];//Translate($min);
221 else
222 $fillmin = $yscale->Translate(0);
224 $cord[$idx++] = $xscale->Translate($xs);
225 $cord[$idx++] = $fillmin;
227 $xt = $xscale->Translate($xs);
228 $yt = $yscale->Translate($this->coords[0][$startpoint]);
229 $cord[$idx++] = $xt;
230 $cord[$idx++] = $yt;
231 $yt_old = $yt;
232 $xt_old = $xt;
233 $y_old = $this->coords[0][$startpoint];
235 $this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);
237 $img->SetColor($this->color);
238 $img->SetLineWeight($this->weight);
239 $img->SetLineStyle($this->line_style);
240 $pnts=$startpoint+1;
241 $firstnonumeric = false;
242 while( $pnts < $numpoints ) {
244 if( $exist_x ) $x=$this->coords[1][$pnts];
245 else $x=$pnts+$textadj;
246 $xt = $xscale->Translate($x);
247 $yt = $yscale->Translate($this->coords[0][$pnts]);
249 $y=$this->coords[0][$pnts];
250 if( $this->step_style ) {
251 // To handle null values within step style we need to record the
252 // first non numeric value so we know from where to start if the
253 // non value is '-'.
254 if( is_numeric($y) ) {
255 $firstnonumeric = false;
256 if( is_numeric($y_old) ) {
257 $img->StyleLine($xt_old,$yt_old,$xt,$yt_old);
258 $img->StyleLine($xt,$yt_old,$xt,$yt);
260 elseif( $y_old == '-' ) {
261 $img->StyleLine($xt_first,$yt_first,$xt,$yt_first);
262 $img->StyleLine($xt,$yt_first,$xt,$yt);
264 else {
265 $yt_old = $yt;
266 $xt_old = $xt;
268 $cord[$idx++] = $xt;
269 $cord[$idx++] = $yt_old;
270 $cord[$idx++] = $xt;
271 $cord[$idx++] = $yt;
273 elseif( $firstnonumeric==false ) {
274 $firstnonumeric = true;
275 $yt_first = $yt_old;
276 $xt_first = $xt_old;
279 else {
280 $tmp1=$y;
281 $prev=$this->coords[0][$pnts-1];
282 if( $tmp1==='' || $tmp1===NULL || $tmp1==='X' ) $tmp1 = 'x';
283 if( $prev==='' || $prev===null || $prev==='X' ) $prev = 'x';
285 if( is_numeric($y) || (is_string($y) && $y != '-') ) {
286 if( is_numeric($y) && (is_numeric($prev) || $prev === '-' ) ) {
287 $img->StyleLineTo($xt,$yt);
289 else {
290 $img->SetStartPoint($xt,$yt);
293 if( $this->filled && $tmp1 !== '-' ) {
294 if( $tmp1 === 'x' ) {
295 $cord[$idx++] = $cord[$idx-3];
296 $cord[$idx++] = $fillmin;
298 elseif( $prev === 'x' ) {
299 $cord[$idx++] = $xt;
300 $cord[$idx++] = $fillmin;
301 $cord[$idx++] = $xt;
302 $cord[$idx++] = $yt;
304 else {
305 $cord[$idx++] = $xt;
306 $cord[$idx++] = $yt;
309 else {
310 if( is_numeric($tmp1) && (is_numeric($prev) || $prev === '-' ) ) {
311 $cord[$idx++] = $xt;
312 $cord[$idx++] = $yt;
316 $yt_old = $yt;
317 $xt_old = $xt;
318 $y_old = $y;
320 $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
322 ++$pnts;
325 if( $this->filled ) {
326 $cord[$idx++] = $xt;
327 if( $min > 0 || $this->fillFromMin )
328 $cord[$idx++] = $yscale->Translate($min);
329 else
330 $cord[$idx++] = $yscale->Translate(0);
331 if( $this->fillgrad ) {
332 $img->SetLineWeight(1);
333 $grad = new Gradient($img);
334 $grad->SetNumColors($this->fillgrad_numcolors);
335 $grad->FilledFlatPolygon($cord,$this->fillgrad_fromcolor,$this->fillgrad_tocolor);
336 $img->SetLineWeight($this->weight);
338 else {
339 $img->SetColor($this->fill_color);
340 $img->FilledPolygon($cord);
342 if( $this->line_weight > 0 ) {
343 $img->SetColor($this->color);
344 $img->Polygon($cord);
348 if(!empty($this->filledAreas)) {
350 $minY = $yscale->Translate($yscale->GetMinVal());
351 $factor = ($this->step_style ? 4 : 2);
353 for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
354 // go through all filled area elements ordered by insertion
355 // fill polygon array
356 $areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
357 $areaCoords[] = $minY;
359 $areaCoords =
360 array_merge($areaCoords,
361 array_slice($cord,
362 $this->filledAreas[$i][0] * $factor,
363 ($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1)) * $factor));
364 $areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
365 $areaCoords[] = $minY; // last y
367 if($this->filledAreas[$i][3]) {
368 $img->SetColor($this->filledAreas[$i][2]);
369 $img->FilledPolygon($areaCoords);
370 $img->SetColor($this->color);
372 // Check if we should draw the frame.
373 // If not we still re-draw the line since it might have been
374 // partially overwritten by the filled area and it doesn't look
375 // very good.
376 // TODO: The behaviour is undefined if the line does not have
377 // any line at the position of the area.
378 if( $this->filledAreas[$i][4] )
379 $img->Polygon($areaCoords);
380 else
381 $img->Polygon($cord);
383 $areaCoords = array();
387 if( $this->mark->type == -1 || $this->mark->show == false )
388 return;
390 for( $pnts=0; $pnts<$numpoints; ++$pnts) {
392 if( $exist_x ) $x=$this->coords[1][$pnts];
393 else $x=$pnts+$textadj;
394 $xt = $xscale->Translate($x);
395 $yt = $yscale->Translate($this->coords[0][$pnts]);
397 if( is_numeric($this->coords[0][$pnts]) ) {
398 if( !empty($this->csimtargets[$pnts]) ) {
399 $this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
400 $this->mark->SetCSIMAlt($this->csimalts[$pnts]);
402 if( $exist_x )
403 $x=$this->coords[1][$pnts];
404 else
405 $x=$pnts;
406 $this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
407 $this->mark->Stroke($img,$xt,$yt);
408 $this->csimareas .= $this->mark->GetCSIMAreas();
409 $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
415 } // Class
418 //===================================================
419 // CLASS AccLinePlot
420 // Description:
421 //===================================================
422 class AccLinePlot extends Plot {
423 var $plots=null,$nbrplots=0,$numpoints=0;
424 var $iStartEndZero=true;
425 //---------------
426 // CONSTRUCTOR
427 function __construct($plots) {
428 $this->plots = $plots;
429 $this->nbrplots = count($plots);
430 $this->numpoints = $plots[0]->numpoints;
432 for($i=0; $i < $this->nbrplots; ++$i ) {
433 $this->LineInterpolate($this->plots[$i]->coords[0]);
437 //---------------
438 // PUBLIC METHODS
439 function Legend(&$graph) {
440 $n=count($this->plots);
441 for($i=0; $i < $n; ++$i )
442 $this->plots[$i]->DoLegend($graph);
445 function Max() {
446 list($xmax) = $this->plots[0]->Max();
447 $nmax=0;
448 $n = count($this->plots);
449 for($i=0; $i < $n; ++$i) {
450 $nc = count($this->plots[$i]->coords[0]);
451 $nmax = max($nmax,$nc);
452 list($x) = $this->plots[$i]->Max();
453 $xmax = Max($xmax,$x);
455 for( $i = 0; $i < $nmax; $i++ ) {
456 // Get y-value for line $i by adding the
457 // individual bars from all the plots added.
458 // It would be wrong to just add the
459 // individual plots max y-value since that
460 // would in most cases give to large y-value.
461 $y=$this->plots[0]->coords[0][$i];
462 for( $j = 1; $j < $this->nbrplots; $j++ ) {
463 $y += $this->plots[ $j ]->coords[0][$i];
465 $ymax[$i] = $y;
467 $ymax = max($ymax);
468 return array($xmax,$ymax);
471 function Min() {
472 $nmax=0;
473 list($xmin,$ysetmin) = $this->plots[0]->Min();
474 $n = count($this->plots);
475 for($i=0; $i < $n; ++$i) {
476 $nc = count($this->plots[$i]->coords[0]);
477 $nmax = max($nmax,$nc);
478 list($x,$y) = $this->plots[$i]->Min();
479 $xmin = Min($xmin,$x);
480 $ysetmin = Min($y,$ysetmin);
482 for( $i = 0; $i < $nmax; $i++ ) {
483 // Get y-value for line $i by adding the
484 // individual bars from all the plots added.
485 // It would be wrong to just add the
486 // individual plots min y-value since that
487 // would in most cases give to small y-value.
488 $y=$this->plots[0]->coords[0][$i];
489 for( $j = 1; $j < $this->nbrplots; $j++ ) {
490 $y += $this->plots[ $j ]->coords[0][$i];
492 $ymin[$i] = $y;
494 $ymin = Min($ysetmin,Min($ymin));
495 return array($xmin,$ymin);
498 // Gets called before any axis are stroked
499 function PreStrokeAdjust(&$graph) {
501 // If another plot type have already adjusted the
502 // offset we don't touch it.
503 // (We check for empty in case the scale is a log scale
504 // and hence doesn't contain any xlabel_offset)
506 if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
507 $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
508 if( $this->center ) {
509 ++$this->numpoints;
510 $a=0.5; $b=0.5;
511 } else {
512 $a=0; $b=0;
514 $graph->xaxis->scale->ticks->SetXLabelOffset($a);
515 $graph->SetTextScaleOff($b);
516 $graph->xaxis->scale->ticks->SupressMinorTickMarks();
521 function SetInterpolateMode($aIntMode) {
522 $this->iStartEndZero=$aIntMode;
525 // Replace all '-' with an interpolated value. We use straightforward
526 // linear interpolation. If the data starts with one or several '-' they
527 // will be replaced by the the first valid data point
528 function LineInterpolate(&$aData) {
530 $n=count($aData);
531 $i=0;
533 // If first point is undefined we will set it to the same as the first
534 // valid data
535 if( $aData[$i]==='-' ) {
536 // Find the first valid data
537 while( $i < $n && $aData[$i]==='-' ) {
538 ++$i;
540 if( $i < $n ) {
541 for($j=0; $j < $i; ++$j ) {
542 if( $this->iStartEndZero )
543 $aData[$i] = 0;
544 else
545 $aData[$j] = $aData[$i];
548 else {
549 // All '-' => Error
550 return false;
554 while($i < $n) {
555 while( $i < $n && $aData[$i] !== '-' ) {
556 ++$i;
558 if( $i < $n ) {
559 $pstart=$i-1;
561 // Now see how long this segment of '-' are
562 while( $i < $n && $aData[$i] === '-' )
563 ++$i;
564 if( $i < $n ) {
565 $pend=$i;
566 $size=$pend-$pstart;
567 $k=($aData[$pend]-$aData[$pstart])/$size;
568 // Replace the segment of '-' with a linear interpolated value.
569 for($j=1; $j < $size; ++$j ) {
570 $aData[$pstart+$j] = $aData[$pstart] + $j*$k ;
573 else {
574 // There are no valid end point. The '-' goes all the way to the end
575 // In that case we just set all the remaining values the the same as the
576 // last valid data point.
577 for( $j=$pstart+1; $j < $n; ++$j )
578 if( $this->iStartEndZero )
579 $aData[$j] = 0;
580 else
581 $aData[$j] = $aData[$pstart] ;
585 return true;
590 // To avoid duplicate of line drawing code here we just
591 // change the y-values for each plot and then restore it
592 // after we have made the stroke. We must do this copy since
593 // it wouldn't be possible to create an acc line plot
594 // with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
595 // since this method would have a side effect.
596 function Stroke(&$img,&$xscale,&$yscale) {
597 $img->SetLineWeight($this->weight);
598 $this->numpoints = count($this->plots[0]->coords[0]);
599 // Allocate array
600 $coords[$this->nbrplots][$this->numpoints]=0;
601 for($i=0; $i<$this->numpoints; $i++) {
602 $coords[0][$i]=$this->plots[0]->coords[0][$i];
603 $accy=$coords[0][$i];
604 for($j=1; $j<$this->nbrplots; ++$j ) {
605 $coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy;
606 $accy = $coords[$j][$i];
609 for($j=$this->nbrplots-1; $j>=0; --$j) {
610 $p=$this->plots[$j];
611 for( $i=0; $i<$this->numpoints; ++$i) {
612 $tmp[$i]=$p->coords[0][$i];
613 $p->coords[0][$i]=$coords[$j][$i];
615 $p->Stroke($img,$xscale,$yscale);
616 for( $i=0; $i<$this->numpoints; ++$i)
617 $p->coords[0][$i]=$tmp[$i];
618 $p->coords[0][]=$tmp;
621 } // Class
624 /* EOF */