Subversion Repositories Applications.gtt

Rev

Rev 61 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
60 jpm 1
<?php
2
/*
3
 * This work is hereby released into the Public Domain.
4
 * To view a copy of the public domain dedication,
5
 * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
6
 * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
7
 *
8
 */
9
 
10
require_once dirname(__FILE__)."/Component.class.php";
11
 
12
 
13
/**
14
 * Graph using X and Y axis
15
 *
16
 * @package Artichow
17
 */
18
abstract class awPlot extends awComponent {
19
 
20
	/**
21
	 * Values for Y axis
22
	 *
23
	 * @var array
24
	 */
25
	protected $datay;
26
 
27
	/**
28
	 * Values for X axis
29
	 *
30
	 * @var array
31
	 */
32
	protected $datax;
33
 
34
	/**
35
	 * Grid properties
36
	 *
37
	 * @var Grid
38
	 */
39
	public $grid;
40
 
41
	/**
42
	 * X axis
43
	 *
44
	 * @var Axis
45
	 */
46
	public $xAxis;
47
 
48
	/**
49
	 * Y axis
50
	 *
51
	 * @var Axis
52
	 */
53
	public $yAxis;
54
 
55
	/**
56
	 * Position of X axis
57
	 *
58
	 * @var int
59
	 */
60
	protected $xAxisPosition = awPlot::BOTTOM;
61
 
62
	/**
63
	 * Set X axis on zero ?
64
	 *
65
	 * @var bool
66
	 */
67
	protected $xAxisZero = TRUE;
68
 
69
	/**
70
	 * Set Y axis on zero ?
71
	 *
72
	 * @var bool
73
	 */
74
	protected $yAxisZero = FALSE;
75
 
76
	/**
77
	 * Position of Y axis
78
	 *
79
	 * @var int
80
	 */
81
	protected $yAxisPosition = awPlot::LEFT;
82
 
83
	/**
84
	 * Change min value for Y axis
85
	 *
86
	 * @var mixed
87
	 */
88
	private $yMin = NULL;
89
 
90
	/**
91
	 * Change max value for Y axis
92
	 *
93
	 * @var mixed
94
	 */
95
	private $yMax = NULL;
96
 
97
	/**
98
	 * Change min value for X axis
99
	 *
100
	 * @var mixed
101
	 */
102
	private $xMin = NULL;
103
 
104
	/**
105
	 * Change max value for X axis
106
	 *
107
	 * @var mixed
108
	 */
109
	private $xMax = NULL;
110
 
111
	/**
112
	 * Left axis
113
	 *
114
	 * @var int
115
	 */
116
	const LEFT = 'left';
117
 
118
	/**
119
	 * Right axis
120
	 *
121
	 * @var int
122
	 */
123
	const RIGHT = 'right';
124
 
125
	/**
126
	 * Top axis
127
	 *
128
	 * @var int
129
	 */
130
	const TOP = 'top';
131
 
132
	/**
133
	 * Bottom axis
134
	 *
135
	 * @var int
136
	 */
137
	const BOTTOM = 'bottom';
138
 
139
	/**
140
	 * Both left/right or top/bottom axis
141
	 *
142
	 * @var int
143
	 */
144
	const BOTH = 'both';
145
 
146
	/**
147
	 * Build the plot
148
	 *
149
	 */
150
	public function __construct() {
151
 
152
		parent::__construct();
153
 
154
		$this->grid = new awGrid;
155
		$this->grid->setBackgroundColor(new awWhite);
156
 
157
		$this->padding->add(20, 0, 0, 20);
158
 
159
		$this->xAxis = new awAxis;
160
		$this->xAxis->addTick('major', new awTick(0, 5));
161
		$this->xAxis->addTick('minor', new awTick(0, 3));
162
		$this->xAxis->setTickStyle(awTick::OUT);
163
		$this->xAxis->label->setFont(new awTuffy(7));
164
 
165
		$this->yAxis = new awAxis;
166
		$this->yAxis->auto(TRUE);
167
		$this->yAxis->addTick('major', new awTick(0, 5));
168
		$this->yAxis->addTick('minor', new awTick(0, 3));
169
		$this->yAxis->setTickStyle(awTick::OUT);
170
		$this->yAxis->setNumberByTick('minor', 'major', 3);
171
		$this->yAxis->label->setFont(new awTuffy(7));
172
		$this->yAxis->title->setAngle(90);
173
 
174
	}
175
 
176
	/**
177
	 * Get plot values
178
	 *
179
	 * @return array
180
	 */
181
	public function getValues() {
182
		return $this->datay;
183
	}
184
 
185
	/**
186
	 * Reduce number of values in the plot
187
	 *
188
	 * @param int $number Reduce number of values to $number
189
	 */
190
	public function reduce($number) {
191
 
192
		$count = count($this->datay);
193
		$ratio = ceil($count / $number);
194
 
195
		if($ratio > 1) {
196
 
197
			$tmpy = $this->datay;
198
			$datay = array();
199
 
200
			$datax = array();
201
			$cbLabel = $this->xAxis->label->getCallbackFunction();
202
 
203
			for($i = 0; $i < $count; $i += $ratio) {
204
 
205
				$slice = array_slice($tmpy, $i, $ratio);
206
				$datay[] = array_sum($slice) / count($slice);
207
 
208
				// Reduce data on X axis if needed
209
				if($cbLabel !== NULL) {
210
					$datax[] = $cbLabel($i + round($ratio / 2));
211
				}
212
 
213
			}
214
 
215
			$this->setValues($datay);
216
 
217
			if($cbLabel !== NULL) {
218
				$this->xAxis->setLabelText($datax);
219
			}
220
 
221
 
222
		}
223
 
224
	}
225
 
226
	/**
227
	 * Count values in the plot
228
	 *
229
	 * @return int
230
	 */
231
	public function getXAxisNumber() {
232
		list($min, $max) = $this->xAxis->getRange();
233
		return ($max - $min + 1);
234
	}
235
 
236
	/**
237
	 * Change X axis
238
	 *
239
	 * @param int $axis
240
	 */
241
	public function setXAxis($axis) {
242
		$this->xAxisPosition = $axis;
243
	}
244
 
245
	/**
246
	 * Get X axis
247
	 *
248
	 * @return int
249
	 */
250
	public function getXAxis() {
251
		return $this->xAxisPosition;
252
	}
253
 
254
	/**
255
	 * Set X axis on zero
256
	 *
257
	 * @param bool $zero
258
	 */
259
	public function setXAxisZero($zero) {
260
		$this->xAxisZero = (bool)$zero;
261
	}
262
 
263
	/**
264
	 * Set Y axis on zero
265
	 *
266
	 * @param bool $zero
267
	 */
268
	public function setYAxisZero($zero) {
269
		$this->yAxisZero = (bool)$zero;
270
	}
271
 
272
	/**
273
	 * Change Y axis
274
	 *
275
	 * @param int $axis
276
	 */
277
	public function setYAxis($axis) {
278
		$this->yAxisPosition = $axis;
279
	}
280
 
281
	/**
282
	 * Get Y axis
283
	 *
284
	 * @return int
285
	 */
286
	public function getYAxis() {
287
		return $this->yAxisPosition;
288
	}
289
 
290
	/**
291
	 * Change min value for Y axis
292
	 * Set NULL for auto selection.
293
	 *
294
	 * @param float $value
295
	 */
296
	public function setYMin($value) {
297
		$this->yMin = $value;
298
		$this->yAxis->auto(FALSE);
299
		$this->updateAxis();
300
	}
301
 
302
	/**
303
	 * Change max value for Y axis
304
	 * Set NULL for auto selection.
305
	 *
306
	 * @param float $value
307
	 */
308
	public function setYMax($value) {
309
		$this->yMax = $value;
310
		$this->yAxis->auto(FALSE);
311
		$this->updateAxis();
312
	}
313
 
314
	/**
315
	 * Change min value for X axis
316
	 * Set NULL for auto selection.
317
	 *
318
	 * @param float $value
319
	 */
320
	public function setXMin($value) {
321
		$this->xMin = $value;
322
		$this->updateAxis();
323
	}
324
 
325
	/**
326
	 * Change max value for X axis
327
	 * Set NULL for auto selection.
328
	 *
329
	 * @param float $value
330
	 */
331
	public function setXMax($value) {
332
		$this->xMax = $value;
333
		$this->updateAxis();
334
	}
335
 
336
	/**
337
	 * Get min value for Y axis
338
	 *
339
	 * @return float $value
340
	 */
341
	public function getYMin() {
342
		if($this->auto) {
343
			if(is_null($this->yMin)) {
344
				$min = array_min($this->datay);
345
				if($min > 0) {
346
					return 0;
347
				}
348
			}
349
		}
350
		return is_null($this->yMin) ? array_min($this->datay) : (float)$this->yMin;
351
	}
352
 
353
	/**
354
	 * Get max value for Y axis
355
	 *
356
	 * @return float $value
357
	 */
358
	public function getYMax() {
359
		if($this->auto) {
360
			if(is_null($this->yMax)) {
361
				$max = array_max($this->datay);
362
				if($max < 0) {
363
					return 0;
364
				}
365
			}
366
		}
367
		return is_null($this->yMax) ? array_max($this->datay) : (float)$this->yMax;
368
	}
369
 
370
	/**
371
	 * Get min value for X axis
372
	 *
373
	 * @return float $value
374
	 */
375
	public function getXMin() {
376
		return floor(is_null($this->xMin) ? array_min($this->datax) : $this->xMin);
377
	}
378
 
379
	/**
380
	 * Get max value for X axis
381
	 *
382
	 * @return float $value
383
	 */
384
	public function getXMax() {
385
		return (ceil(is_null($this->xMax) ? array_max($this->datax) : (float)$this->xMax)) + ($this->getXCenter() ? 1 : 0);
386
	}
387
 
388
	/**
389
	 * Get min value with spaces for Y axis
390
	 *
391
	 * @return float $value
392
	 */
393
	public function getRealYMin() {
394
		$min = $this->getYMin();
395
		if($this->space->bottom !== NULL) {
396
			$interval = ($this->getYMax() - $min) * $this->space->bottom / 100;
397
			return $min - $interval;
398
		} else {
399
			return is_null($this->yMin) ? $min : (float)$this->yMin;
400
		}
401
	}
402
 
403
	/**
404
	 * Get max value with spaces for Y axis
405
	 *
406
	 * @return float $value
407
	 */
408
	public function getRealYMax() {
409
		$max = $this->getYMax();
410
		if($this->space->top !== NULL) {
411
			$interval = ($max - $this->getYMin()) * $this->space->top / 100;
412
			return $max + $interval;
413
		} else {
414
			return is_null($this->yMax) ? $max : (float)$this->yMax;
415
		}
416
	}
417
 
418
	public function init(awDriver $driver) {
419
 
420
		list($x1, $y1, $x2, $y2) = $this->getPosition();
421
 
422
		// Get space informations
423
		list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
424
 
425
		$this->xAxis->setPadding($leftSpace, $rightSpace);
426
 
427
		if($this->space->bottom > 0 or $this->space->top > 0) {
428
 
429
			list($min, $max) = $this->yAxis->getRange();
430
			$interval = $max - $min;
431
 
432
			$this->yAxis->setRange(
433
				$min - $interval * $this->space->bottom / 100,
434
				$max + $interval * $this->space->top / 100
435
			);
436
 
437
		}
438
 
439
		// Auto-scaling mode
440
		$this->yAxis->autoScale();
441
 
442
		// Number of labels is not specified
443
		if($this->yAxis->getLabelNumber() === NULL) {
444
			$number = round(($y2 - $y1) / 75) + 2;
445
			$this->yAxis->setLabelNumber($number);
446
		}
447
 
448
		$this->xAxis->line->setX($x1, $x2);
449
		$this->yAxis->line->setY($y2, $y1);
450
 
451
		// Set ticks
452
 
453
		$this->xAxis->tick('major')->setNumber($this->getXAxisNumber());
454
		$this->yAxis->tick('major')->setNumber($this->yAxis->getLabelNumber());
455
 
456
 
457
		// Center X axis on zero
458
		if($this->xAxisZero) {
459
			$this->xAxis->setYCenter($this->yAxis, 0);
460
		}
461
 
462
		// Center Y axis on zero
463
		if($this->yAxisZero) {
464
			$this->yAxis->setXCenter($this->xAxis, 0);
465
		}
466
 
467
		// Set axis labels
468
		$labels = array();
469
 
470
		list($xMin, $xMax) = $this->xAxis->getRange();
471
		for($i = $xMin; $i <= $xMax; $i++) {
472
			$labels[] = $i;
473
		}
474
		$this->xAxis->label->set($labels);
475
 
476
		parent::init($driver);
477
 
478
		list($x1, $y1, $x2, $y2) = $this->getPosition();
479
 
480
		list($leftSpace, $rightSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
481
 
482
		// Create the grid
483
		$this->createGrid();
484
 
485
		// Draw the grid
486
		$this->grid->setSpace($leftSpace, $rightSpace, 0, 0);
487
		$this->grid->draw($driver, $x1, $y1, $x2, $y2);
488
 
489
	}
490
 
491
	public function drawEnvelope(awDriver $driver) {
492
 
493
		list($x1, $y1, $x2, $y2) = $this->getPosition();
494
 
495
		if($this->getXCenter()) {
496
			$size = $this->xAxis->getDistance(0, 1);
497
			$this->xAxis->label->move($size / 2, 0);
498
			$this->xAxis->label->hideLast(TRUE);
499
		}
500
 
501
		// Draw top axis
502
		if($this->xAxisPosition === awPlot::TOP or $this->xAxisPosition === awPlot::BOTH) {
503
			$top = clone $this->xAxis;
504
			if($this->xAxisZero === FALSE) {
505
				$top->line->setY($y1, $y1);
506
			}
507
			$top->label->setAlign(NULL, awLabel::TOP);
508
			$top->label->move(0, -3);
509
			$top->title->move(0, -25);
510
			$top->draw($driver);
511
		}
512
 
513
		// Draw bottom axis
514
		if($this->xAxisPosition === awPlot::BOTTOM or $this->xAxisPosition === awPlot::BOTH) {
515
			$bottom = clone $this->xAxis;
516
			if($this->xAxisZero === FALSE) {
517
				$bottom->line->setY($y2, $y2);
518
			}
519
			$bottom->label->setAlign(NULL, awLabel::BOTTOM);
520
			$bottom->label->move(0, 3);
521
			$bottom->reverseTickStyle();
522
			$bottom->title->move(0, 25);
523
			$bottom->draw($driver);
524
		}
525
 
526
		// Draw left axis
527
		if($this->yAxisPosition === awPlot::LEFT or $this->yAxisPosition === awPlot::BOTH) {
528
			$left = clone $this->yAxis;
529
			if($this->yAxisZero === FALSE) {
530
				$left->line->setX($x1, $x1);
531
			}
532
			$left->label->setAlign(awLabel::RIGHT);
533
			$left->label->move(-6, 0);
534
			$left->title->move(-25, 0);
535
			$left->draw($driver);
536
		}
537
 
538
		// Draw right axis
539
		if($this->yAxisPosition === awPlot::RIGHT or $this->yAxisPosition === awPlot::BOTH) {
540
			$right = clone $this->yAxis;
541
			if($this->yAxisZero === FALSE) {
542
				$right->line->setX($x2, $x2);
543
			}
544
			$right->label->setAlign(awLabel::LEFT);
545
			$right->label->move(6, 0);
546
			$right->reverseTickStyle();
547
			$right->title->move(25, 0);
548
			$right->draw($driver);
549
		}
550
 
551
	}
552
 
553
	protected function createGrid() {
554
 
555
		$max = $this->getRealYMax();
556
		$min = $this->getRealYMin();
557
 
558
		$number = $this->yAxis->getLabelNumber() - 1;
559
 
560
		if($number < 1) {
561
			return;
562
		}
563
 
564
		// Horizontal lines of the grid
565
 
566
		$h = array();
567
		for($i = 0; $i <= $number; $i++) {
568
			$h[] = $i / $number;
569
		}
570
 
571
		// Vertical lines
572
 
573
		$major = $this->yAxis->tick('major');
574
		$interval = $major->getInterval();
575
		$number = $this->getXAxisNumber() - 1;
576
 
577
		$w = array();
578
 
579
		if($number > 0) {
580
 
581
			for($i = 0; $i <= $number; $i++) {
582
				if($i%$interval === 0) {
583
					$w[] = $i / $number;
584
				}
585
			}
586
 
587
		}
588
 
589
		$this->grid->setGrid($w, $h);
590
 
591
	}
592
 
593
	/**
594
	 * Change values of Y axis
595
	 * This method ignores not numeric values
596
	 *
597
	 * @param array $datay
598
	 * @param array $datax
599
	 */
600
	public function setValues($datay, $datax = NULL) {
601
 
602
		$this->checkArray($datay);
603
 
604
		foreach($datay as $key => $value) {
605
			unset($datay[$key]);
606
			$datay[(int)$key] = $value;
607
		}
608
 
609
		if($datax === NULL) {
610
			$datax = array();
611
			for($i = 0; $i < count($datay); $i++) {
612
				$datax[] = $i;
613
			}
614
		} else {
615
			foreach($datax as $key => $value) {
616
				unset($datax[$key]);
617
				$datax[(int)$key] = $value;
618
			}
619
		}
620
 
621
		$this->checkArray($datax);
622
 
623
		if(count($datay) === count($datax)) {
624
 
625
			// Set values
626
			$this->datay = $datay;
627
			$this->datax = $datax;
628
			// Update axis with the new awvalues
629
			$this->updateAxis();
630
		} else {
631
			awImage::drawError("Class Plot: Plots must have the same number of X and Y points.");
632
		}
633
 
634
	}
635
 
636
	/**
637
	 * Return begin and end values
638
	 *
639
	 * @return array
640
	 */
641
	protected function getLimit() {
642
 
643
		$i = 0;
644
		while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) {
645
			$i++;
646
		}
647
		$start = $i;
648
		$i = count($this->datay) - 1;
649
		while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) {
650
			$i--;
651
		}
652
		$stop = $i;
653
 
654
		return array($start, $stop);
655
 
656
	}
657
 
658
	/**
659
	 * Return TRUE if labels must be centered on X axis, FALSE otherwise
660
	 *
661
	 * @return bool
662
	 */
663
	abstract public function getXCenter();
664
 
665
	private function updateAxis() {
666
 
667
		$this->xAxis->setRange(
668
			$this->getXMin(),
669
			$this->getXMax()
670
		);
671
		$this->yAxis->setRange(
672
			$this->getRealYMin(),
673
			$this->getRealYMax()
674
		);
675
 
676
	}
677
 
678
	private function checkArray(&$array) {
679
 
680
		if(is_array($array) === FALSE) {
681
			awImage::drawError("Class Plot: You tried to set a value that is not an array.");
682
		}
683
 
684
		foreach($array as $key => $value) {
685
			if(is_numeric($value) === FALSE and is_null($value) === FALSE) {
686
				awImage::drawError("Class Plot: Expected numeric values for the plot.");
687
			}
688
		}
689
 
690
		if(count($array) < 1) {
691
			awImage::drawError("Class Plot: Your plot must have at least 1 value.");
692
		}
693
 
694
	}
695
 
696
}
697
 
698
registerClass('Plot', TRUE);
699
 
700
class awPlotAxis {
701
 
702
	/**
703
	 * Left axis
704
	 *
705
	 * @var Axis
706
	 */
707
	public $left;
708
 
709
	/**
710
	 * Right axis
711
	 *
712
	 * @var Axis
713
	 */
714
	public $right;
715
 
716
	/**
717
	 * Top axis
718
	 *
719
	 * @var Axis
720
	 */
721
	public $top;
722
 
723
	/**
724
	 * Bottom axis
725
	 *
726
	 * @var Axis
727
	 */
728
	public $bottom;
729
 
730
	/**
731
	 * Build the group of axis
732
	 */
733
	public function __construct() {
734
 
735
		$this->left = new awAxis;
736
		$this->left->auto(TRUE);
737
		$this->left->label->setAlign(awLabel::RIGHT);
738
		$this->left->label->move(-6, 0);
739
		$this->yAxis($this->left);
740
		$this->left->setTickStyle(awTick::OUT);
741
		$this->left->title->move(-25, 0);
742
 
743
		$this->right = new awAxis;
744
		$this->right->auto(TRUE);
745
		$this->right->label->setAlign(awLabel::LEFT);
746
		$this->right->label->move(6, 0);
747
		$this->yAxis($this->right);
748
		$this->right->setTickStyle(awTick::IN);
749
		$this->right->title->move(25, 0);
750
 
751
		$this->top = new awAxis;
752
		$this->top->label->setAlign(NULL, awLabel::TOP);
753
		$this->top->label->move(0, -3);
754
		$this->xAxis($this->top);
755
		$this->top->setTickStyle(awTick::OUT);
756
		$this->top->title->move(0, -25);
757
 
758
		$this->bottom = new awAxis;
759
		$this->bottom->label->setAlign(NULL, awLabel::BOTTOM);
760
		$this->bottom->label->move(0, 3);
761
		$this->xAxis($this->bottom);
762
		$this->bottom->setTickStyle(awTick::IN);
763
		$this->bottom->title->move(0, 25);
764
 
765
	}
766
 
767
	protected function xAxis(awAxis $axis) {
768
 
769
		$axis->addTick('major', new awTick(0, 5));
770
		$axis->addTick('minor', new awTick(0, 3));
771
		$axis->label->setFont(new awTuffy(7));
772
 
773
	}
774
 
775
	protected function yAxis(awAxis $axis) {
776
 
777
		$axis->addTick('major', new awTick(0, 5));
778
		$axis->addTick('minor', new awTick(0, 3));
779
		$axis->setNumberByTick('minor', 'major', 3);
780
		$axis->label->setFont(new awTuffy(7));
781
		$axis->title->setAngle(90);
782
 
783
	}
784
 
785
}
786
 
787
registerClass('PlotAxis');
788
 
789
/**
790
 * A graph with axis can contain some groups of components
791
 *
792
 * @package Artichow
793
 */
794
class awPlotGroup extends awComponentGroup {
795
 
796
	/**
797
	 * Grid properties
798
	 *
799
	 * @var Grid
800
	 */
801
	public $grid;
802
 
803
	/**
804
	 * Left, right, top and bottom axis
805
	 *
806
	 * @var PlotAxis
807
	 */
808
	public $axis;
809
 
810
	/**
811
	 * Set the X axis on zero
812
	 *
813
	 * @var bool
814
	 */
815
	protected $xAxisZero = TRUE;
816
 
817
	/**
818
	 * Set the Y axis on zero
819
	 *
820
	 * @var bool
821
	 */
822
	protected $yAxisZero = FALSE;
823
 
824
	/**
825
	 * Real axis used for Y axis
826
	 *
827
	 * @var string
828
	 */
829
	private $yRealAxis = awPlot::LEFT;
830
 
831
	/**
832
	 * Real axis used for X axis
833
	 *
834
	 * @var string
835
	 */
836
	private $xRealAxis = awPlot::BOTTOM;
837
 
838
	/**
839
	 * Change min value for Y axis
840
	 *
841
	 * @var mixed
842
	 */
843
	private $yMin = NULL;
844
 
845
	/**
846
	 * Change max value for Y axis
847
	 *
848
	 * @var mixed
849
	 */
850
	private $yMax = NULL;
851
 
852
	/**
853
	 * Change min value for X axis
854
	 *
855
	 * @var mixed
856
	 */
857
	private $xMin = NULL;
858
 
859
	/**
860
	 * Change max value for X axis
861
	 *
862
	 * @var mixed
863
	 */
864
	private $xMax = NULL;
865
 
866
	/**
867
	 * Build the PlotGroup
868
	 *
869
	 */
870
	public function __construct() {
871
 
872
		parent::__construct();
873
 
874
		$this->grid = new awGrid;
875
		$this->grid->setBackgroundColor(new awWhite);
876
 
877
		$this->axis = new awPlotAxis;
878
 
879
	}
880
 
881
	/**
882
	 * Set the X axis on zero or not
883
	 *
884
	 * @param bool $zero
885
	 */
886
	public function setXAxisZero($zero) {
887
		$this->xAxisZero = (bool)$zero;
888
	}
889
 
890
	/**
891
	 * Set the Y axis on zero or not
892
	 *
893
	 * @param bool $zero
894
	 */
895
	public function setYAxisZero($zero) {
896
		$this->yAxisZero = (bool)$zero;
897
	}
898
 
899
	/**
900
	 * Change min value for Y axis
901
	 * Set NULL for auto selection.
902
	 *
903
	 * @param float $value
904
	 */
905
	public function setYMin($value) {
906
		$this->axis->left->auto(FALSE);
907
		$this->axis->right->auto(FALSE);
908
		$this->yMin = $value;
909
	}
910
 
911
	/**
912
	 * Change max value for Y axis
913
	 * Set NULL for auto selection.
914
	 *
915
	 * @param float $value
916
	 */
917
	public function setYMax($value) {
918
		$this->axis->left->auto(FALSE);
919
		$this->axis->right->auto(FALSE);
920
		$this->yMax = $value;
921
	}
922
 
923
	/**
924
	 * Change min value for X axis
925
	 * Set NULL for auto selection.
926
	 *
927
	 * @param float $value
928
	 */
929
	public function setXMin($value) {
930
		$this->xMin = $value;
931
	}
932
 
933
	/**
934
	 * Change max value for X axis
935
	 * Set NULL for auto selection.
936
	 *
937
	 * @param float $value
938
	 */
939
	public function setXMax($value) {
940
		$this->xMax = $value;
941
	}
942
 
943
	/**
944
	 * Get min value for X axis
945
	 *
946
	 * @return float $value
947
	 */
948
	public function getXMin() {
949
 
950
		return $this->getX('min');
951
 
952
	}
953
 
954
	/**
955
	 * Get max value for X axis
956
	 *
957
	 * @return float $value
958
	 */
959
	public function getXMax() {
960
 
961
		return $this->getX('max');
962
 
963
	}
964
 
965
	private function getX($type) {
966
 
967
		switch($type) {
968
			case 'max' :
969
				if($this->xMax !== NULL) {
970
					return $this->xMax;
971
				}
972
				break;
973
			case 'min' :
974
				if($this->xMin !== NULL) {
975
					return $this->xMin;
976
				}
977
				break;
978
		}
979
 
980
		$value = NULL;
981
		$get = 'getX'.ucfirst($type);
982
 
983
		for($i = 0; $i < count($this->components); $i++) {
984
 
985
			$component = $this->components[$i];
986
 
987
			if($value === NULL) {
988
				$value = $component->$get();
989
			} else {
990
				$value = $type($value, $component->$get());
991
			}
992
 
993
		}
994
 
995
		return $value;
996
 
997
	}
998
 
999
	/**
1000
	 * Get min value with spaces for Y axis
1001
	 *
1002
	 * @param string $axis Axis name
1003
	 * @return float $value
1004
	 */
1005
	public function getRealYMin($axis = NULL) {
1006
 
1007
		if($axis === NULL) {
1008
			return NULL;
1009
		}
1010
 
1011
		$min = $this->getRealY('min', $axis);
1012
		$max = $this->getRealY('max', $axis);
1013
 
1014
		if($this->space->bottom !== NULL) {
1015
			$interval = ($min - $max) * $this->space->bottom / 100;
1016
			return $min + $interval;
1017
		} else {
1018
			return $min;
1019
		}
1020
 
1021
	}
1022
 
1023
	/**
1024
	 * Get max value with spaces for Y axis
1025
	 *
1026
	 * @param string $axis Axis name
1027
	 * @return float $value
1028
	 */
1029
	public function getRealYMax($axis = NULL) {
1030
 
1031
		if($axis === NULL) {
1032
			return NULL;
1033
		}
1034
 
1035
		$min = $this->getRealY('min', $axis);
1036
		$max = $this->getRealY('max', $axis);
1037
 
1038
		if($this->space->top !== NULL) {
1039
			$interval = ($max - $min) * $this->space->top / 100;
1040
			return $max + $interval;
1041
		} else {
1042
			return $max;
1043
		}
1044
 
1045
	}
1046
 
1047
	private function getRealY($type, $axis) {
1048
 
1049
		switch($type) {
1050
			case 'max' :
1051
				if($this->yMax !== NULL) {
1052
					return $this->yMax;
1053
				}
1054
				break;
1055
			case 'min' :
1056
				if($this->yMin !== NULL) {
1057
					return $this->yMin;
1058
				}
1059
				break;
1060
		}
1061
 
1062
		$value = NULL;
1063
		$get = 'getY'.ucfirst($type);
1064
 
1065
		for($i = 0; $i < count($this->components); $i++) {
1066
 
1067
			$component = $this->components[$i];
1068
 
1069
			switch($axis) {
1070
 
1071
				case awPlot::LEFT :
1072
				case awPlot::RIGHT :
1073
					$test = ($component->getYAxis() === $axis);
1074
					break;
1075
				default :
1076
					$test = FALSE;
1077
 
1078
			}
1079
 
1080
			if($test) {
1081
 
1082
				$auto = $component->yAxis->isAuto();
1083
				$this->axis->{$axis}->auto($auto);
1084
 
1085
				if($value === NULL) {
1086
					$value = $component->$get();
1087
				} else {
1088
					$value = $type($value, $component->$get());
1089
				}
1090
 
1091
			}
1092
 
1093
		}
1094
 
1095
		return $value;
1096
 
1097
	}
1098
 
1099
	public function init(awDriver $driver) {
1100
 
1101
		list($x1, $y1, $x2, $y2) = $this->getPosition();
1102
 
1103
		// Get PlotGroup space
1104
		list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
1105
 
1106
		// Count values in the group
1107
		$values = $this->getXAxisNumber();
1108
 
1109
		// Init the PlotGroup
1110
		$this->axis->top->line->setX($x1, $x2);
1111
		$this->axis->bottom->line->setX($x1, $x2);
1112
		$this->axis->left->line->setY($y2, $y1);
1113
		$this->axis->right->line->setY($y2, $y1);
1114
 
1115
		$this->axis->top->setPadding($leftSpace, $rightSpace);
1116
		$this->axis->bottom->setPadding($leftSpace, $rightSpace);
1117
 
1118
		$xMin = $this->getXMin();
1119
		$xMax = $this->getXMax();
1120
 
1121
		$this->axis->top->setRange($xMin, $xMax);
1122
		$this->axis->bottom->setRange($xMin, $xMax);
1123
 
1124
		for($i = 0; $i < count($this->components); $i++) {
1125
 
1126
 
1127
			$component = $this->components[$i];
1128
 
1129
						$component->auto($this->auto);
1130
 
1131
			// Copy space to the component
1132
 
1133
			$component->setSpace($this->space->left, $this->space->right, $this->space->top, $this->space->bottom);
1134
 
1135
			$component->xAxis->setPadding($leftSpace, $rightSpace);
1136
			$component->xAxis->line->setX($x1, $x2);
1137
 
1138
			$component->yAxis->line->setY($y2, $y1);
1139
 
1140
		}
1141
 
1142
		// Set Y axis range
1143
		foreach(array('left', 'right') as $axis) {
1144
 
1145
			if($this->isAxisUsed($axis)) {
1146
 
1147
				$min = $this->getRealYMin($axis);
1148
				$max = $this->getRealYMax($axis);
1149
 
1150
				$interval = $max - $min;
1151
 
1152
				$this->axis->{$axis}->setRange(
1153
					$min - $interval * $this->space->bottom / 100,
1154
					$max + $interval * $this->space->top / 100
1155
				);
1156
 
1157
				// Auto-scaling mode
1158
				if($this->axis->{$axis}->isAuto()) {
1159
					$this->axis->{$axis}->autoScale();
1160
				}
1161
 
1162
			}
1163
 
1164
		}
1165
 
1166
		if($this->axis->left->getLabelNumber() === NULL) {
1167
			$number = round(($y2 - $y1) / 75) + 2;
1168
			$this->axis->left->setLabelNumber($number);
1169
		}
1170
 
1171
		if($this->axis->right->getLabelNumber() === NULL) {
1172
			$number = round(($y2 - $y1) / 75) + 2;
1173
			$this->axis->right->setLabelNumber($number);
1174
		}
1175
 
1176
		// Center labels on X axis if needed
1177
		$test = array(awPlot::TOP => FALSE, awPlot::BOTTOM => FALSE);
1178
 
1179
		for($i = 0; $i < count($this->components); $i++) {
1180
 
1181
 
1182
			$component = $this->components[$i];
1183
 
1184
 
1185
			if($component->getValues() !== NULL) {
1186
 
1187
				$axis = $component->getXAxis();
1188
 
1189
				if($test[$axis] === FALSE) {
1190
 
1191
					// Center labels for bar plots
1192
					if($component->getXCenter()) {
1193
						$size = $this->axis->{$axis}->getDistance(0, 1);
1194
						$this->axis->{$axis}->label->move($size / 2, 0);
1195
						$this->axis->{$axis}->label->hideLast(TRUE);
1196
						$test[$axis] = TRUE;
1197
					}
1198
 
1199
				}
1200
 
1201
			}
1202
 
1203
 
1204
		}
1205
 
1206
		// Set axis labels
1207
		$labels = array();
1208
		for($i = $xMin; $i <= $xMax; $i++) {
1209
			$labels[] = $i;
1210
		}
1211
		if($this->axis->top->label->count() === 0) {
1212
			$this->axis->top->label->set($labels);
1213
		}
1214
		if($this->axis->bottom->label->count() === 0) {
1215
			$this->axis->bottom->label->set($labels);
1216
		}
1217
 
1218
		// Set ticks
1219
 
1220
		$this->axis->top->tick('major')->setNumber($values);
1221
		$this->axis->bottom->tick('major')->setNumber($values);
1222
		$this->axis->left->tick('major')->setNumber($this->axis->left->getLabelNumber());
1223
		$this->axis->right->tick('major')->setNumber($this->axis->right->getLabelNumber());
1224
 
1225
 
1226
		// Set X axis on zero
1227
		if($this->xAxisZero) {
1228
			$axis = $this->selectYAxis();
1229
			$this->axis->bottom->setYCenter($axis, 0);
1230
			$this->axis->top->setYCenter($axis, 0);
1231
		}
1232
 
1233
		// Set Y axis on zero
1234
		if($this->yAxisZero) {
1235
			$axis = $this->selectXAxis();
1236
			$this->axis->left->setXCenter($axis, 1);
1237
			$this->axis->right->setXCenter($axis, 1);
1238
		}
1239
 
1240
		parent::init($driver);
1241
 
1242
		list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
1243
 
1244
		// Create the grid
1245
		$this->createGrid();
1246
 
1247
		// Draw the grid
1248
		$this->grid->setSpace($leftSpace, $rightSpace, 0, 0);
1249
		$this->grid->draw($driver, $x1, $y1, $x2, $y2);
1250
 
1251
	}
1252
 
1253
	public function drawComponent(awDriver $driver, $x1, $y1, $x2, $y2, $aliasing) {
1254
 
1255
		$xMin = $this->getXMin();
1256
		$xMax = $this->getXMax();
1257
 
1258
		$maxLeft = $this->getRealYMax(awPlot::LEFT);
1259
		$maxRight = $this->getRealYMax(awPlot::RIGHT);
1260
 
1261
		$minLeft = $this->getRealYMin(awPlot::LEFT);
1262
		$minRight = $this->getRealYMin(awPlot::RIGHT);
1263
 
1264
		foreach($this->components as $component) {
1265
 
1266
			$min = $component->getYMin();
1267
			$max = $component->getYMax();
1268
 
1269
			// Set component minimum and maximum
1270
			if($component->getYAxis() === awPlot::LEFT) {
1271
 
1272
				list($min, $max) = $this->axis->left->getRange();
1273
 
1274
				$component->setYMin($min);
1275
				$component->setYMax($max);
1276
 
1277
			} else {
1278
 
1279
				list($min, $max) = $this->axis->right->getRange();
1280
 
1281
				$component->setYMin($min);
1282
				$component->setYMax($max);
1283
 
1284
			}
1285
 
1286
			$component->setXAxisZero($this->xAxisZero);
1287
			$component->setYAxisZero($this->yAxisZero);
1288
 
1289
			$component->xAxis->setRange($xMin, $xMax);
1290
 
1291
			$component->drawComponent(
1292
				$driver,
1293
				$x1, $y1,
1294
				$x2, $y2,
1295
				$aliasing
1296
			);
1297
 
1298
			$component->setYMin($min);
1299
			$component->setYMax($max);
1300
 
1301
		}
1302
 
1303
	}
1304
 
1305
	public function drawEnvelope(awDriver $driver) {
1306
 
1307
		list($x1, $y1, $x2, $y2) = $this->getPosition();
1308
 
1309
		// Hide unused axis
1310
		foreach(array(awPlot::LEFT, awPlot::RIGHT, awPlot::TOP, awPlot::BOTTOM) as $axis) {
1311
			if($this->isAxisUsed($axis) === FALSE) {
1312
				$this->axis->{$axis}->hide(TRUE);
1313
			}
1314
		}
1315
 
1316
		// Draw top axis
1317
		$top = $this->axis->top;
1318
		if($this->xAxisZero === FALSE) {
1319
			$top->line->setY($y1, $y1);
1320
		}
1321
		$top->draw($driver);
1322
 
1323
		// Draw bottom axis
1324
		$bottom = $this->axis->bottom;
1325
		if($this->xAxisZero === FALSE) {
1326
			$bottom->line->setY($y2, $y2);
1327
		}
1328
		$bottom->draw($driver);
1329
 
1330
		// Draw left axis
1331
		$left = $this->axis->left;
1332
		if($this->yAxisZero === FALSE) {
1333
			$left->line->setX($x1, $x1);
1334
		}
1335
		$left->draw($driver);
1336
 
1337
		// Draw right axis
1338
		$right = $this->axis->right;
1339
		if($this->yAxisZero === FALSE) {
1340
			$right->line->setX($x2, $x2);
1341
		}
1342
		$right->draw($driver);
1343
 
1344
	}
1345
 
1346
	/**
1347
	 * Is the specified axis used ?
1348
	 *
1349
	 * @param string $axis Axis name
1350
	 * @return bool
1351
	 */
1352
	protected function isAxisUsed($axis) {
1353
 
1354
		for($i = 0; $i < count($this->components); $i++) {
1355
 
1356
			$component = $this->components[$i];
1357
 
1358
			switch($axis) {
1359
 
1360
				case awPlot::LEFT :
1361
				case awPlot::RIGHT :
1362
					if($component->getYAxis() === $axis) {
1363
						return TRUE;
1364
					}
1365
					break;
1366
 
1367
				case awPlot::TOP :
1368
				case awPlot::BOTTOM :
1369
					if($component->getXAxis() === $axis) {
1370
						return TRUE;
1371
					}
1372
					break;
1373
 
1374
			}
1375
 
1376
		}
1377
 
1378
		return FALSE;
1379
 
1380
	}
1381
 
1382
	protected function createGrid() {
1383
 
1384
		$max = $this->getRealYMax(awPlot::LEFT);
1385
		$min = $this->getRealYMin(awPlot::RIGHT);
1386
 
1387
		// Select axis (left if possible, right otherwise)
1388
		$axis = $this->selectYAxis();
1389
 
1390
		$number = $axis->getLabelNumber() - 1;
1391
 
1392
		if($number < 1) {
1393
			return;
1394
		}
1395
 
1396
		// Horizontal lines of grid
1397
 
1398
		$h = array();
1399
		for($i = 0; $i <= $number; $i++) {
1400
			$h[] = $i / $number;
1401
		}
1402
 
1403
		// Vertical lines
1404
 
1405
		$major = $axis->tick('major');
1406
		$interval = $major->getInterval();
1407
		$number = $this->getXAxisNumber() - 1;
1408
 
1409
		$w = array();
1410
 
1411
		if($number > 0) {
1412
 
1413
			for($i = 0; $i <= $number; $i++) {
1414
				if($i%$interval === 0) {
1415
					$w[] = $i / $number;
1416
				}
1417
			}
1418
 
1419
		}
1420
 
1421
		$this->grid->setGrid($w, $h);
1422
 
1423
	}
1424
 
1425
	protected function selectYAxis(){
1426
 
1427
		// Select axis (left if possible, right otherwise)
1428
		if($this->isAxisUsed(awPlot::LEFT)) {
1429
			$axis = $this->axis->left;
1430
		} else {
1431
			$axis = $this->axis->right;
1432
		}
1433
 
1434
		return $axis;
1435
 
1436
	}
1437
 
1438
	protected function selectXAxis(){
1439
 
1440
		// Select axis (bottom if possible, top otherwise)
1441
		if($this->isAxisUsed(awPlot::BOTTOM)) {
1442
			$axis = $this->axis->bottom;
1443
		} else {
1444
			$axis = $this->axis->top;
1445
		}
1446
 
1447
		return $axis;
1448
 
1449
	}
1450
 
1451
	protected function getXAxisNumber() {
1452
		$offset = $this->components[0];
1453
		$max = $offset->getXAxisNumber();
1454
		for($i = 1; $i < count($this->components); $i++) {
1455
			$offset = $this->components[$i];
1456
			$max = max($max, $offset->getXAxisNumber());
1457
		}
1458
		return $max;
1459
	}
1460
 
1461
}
1462
 
1463
registerClass('PlotGroup');
1464
?>