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__)."/../Graph.class.php";
11
 
12
/**
13
 * Handle axis
14
 *
15
 * @package Artichow
16
 */
17
class awAxis {
18
 
19
	/**
20
	 * Axis line
21
	 *
22
	 * @var Line
23
	 */
24
	public $line;
25
 
26
	/**
27
	 * Axis labels
28
	 *
29
	 * @var Label
30
	 */
31
	public $label;
32
 
33
	/**
34
	 * Axis title
35
	 *
36
	 * @var Label
37
	 */
38
	public $title;
39
 
40
	/**
41
	 * Title position
42
	 *
43
	 * @var float
44
	 */
45
	protected $titlePosition = 0.5;
46
 
47
	/**
48
	 * Labels number
49
	 *
50
	 * @var int
51
	 */
52
	protected $labelNumber;
53
 
54
	/**
55
	 * Axis ticks
56
	 *
57
	 * @var array
58
	 */
59
	protected $ticks = array();
60
 
61
	/**
62
	 * Axis and ticks color
63
	 *
64
	 * @var Color
65
	 */
66
	protected $color;
67
 
68
	/**
69
	 * Axis left and right padding
70
	 *
71
	 * @var Side
72
	 */
73
	protected $padding;
74
 
75
	/**
76
	 * Axis range
77
	 *
78
	 * @var array
79
	 */
80
	protected $range;
81
 
82
	/**
83
	 * Hide axis
84
	 *
85
	 * @var bool
86
	 */
87
	protected $hide = FALSE;
88
 
89
	/**
90
	 * Auto-scaling mode
91
	 *
92
	 * @var bool
93
	 */
94
	protected $auto = TRUE;
95
 
96
	/**
97
	 * Axis range callback function
98
	 *
99
	 * @var array
100
	 */
101
	protected $rangeCallback = array(
102
		'toValue' => 'toProportionalValue',
103
		'toPosition' => 'toProportionalPosition'
104
	);
105
 
106
	/**
107
	 * Build the axis
108
	 *
109
	 * @param float $min Begin of the range of the axis
110
	 * @param float $max End of the range of the axis
111
	 */
112
	public function __construct($min = NULL, $max = NULL) {
113
 
114
		$this->line = new awVector(
115
			new awPoint(0, 0),
116
			new awPoint(0, 0)
117
		);
118
 
119
		$this->label = new awLabel;
120
		$this->padding = new awSide;
121
 
122
		$this->title = new awLabel(
123
			NULL,
124
			NULL,
125
			NULL,
126
 
127
		);
128
 
129
		$this->setColor(new awBlack);
130
 
131
		if($min !== NULL and $max !== NULL) {
132
			$this->setRange($min, $max);
133
		}
134
 
135
	}
136
 
137
	/**
138
	 * Enable/disable auto-scaling mode
139
	 *
140
	 * @param bool $auto
141
	 */
142
	public function auto($auto) {
143
		$this->auto = (bool)$auto;
144
	}
145
 
146
	/**
147
	 * Get auto-scaling mode status
148
	 *
149
	 * @return bool
150
	 */
151
	public function isAuto() {
152
		return $this->auto;
153
	}
154
 
155
	/**
156
	 * Hide axis
157
	 *
158
	 * @param bool $hide
159
	 */
160
	public function hide($hide = TRUE) {
161
		$this->hide = (bool)$hide;
162
	}
163
 
164
	/**
165
	 * Show axis
166
	 *
167
	 * @param bool $show
168
	 */
169
	public function show($show = TRUE) {
170
		$this->hide = !(bool)$show;
171
	}
172
 
173
	/**
174
	 * Return a tick object from its name
175
	 *
176
	 * @param string $name Tick object name
177
	 * @return Tick
178
	 */
179
	public function tick($name) {
180
 
181
		return array_key_exists($name, $this->ticks) ? $this->ticks[$name] : NULL;
182
 
183
			}
184
 
185
	/**
186
	 * Add a tick object
187
	 *
188
	 * @param string $name Tick object name
189
	 * @param awTick $tick Tick object
190
	 */
191
	public function addTick($name, awTick $tick) {
192
 
193
		$this->ticks[$name] = $tick;
194
 
195
			}
196
 
197
	/**
198
	 * Delete a tick object
199
	 *
200
	 * @param string $name Tick object name
201
	 */
202
	public function deleteTick($name) {
203
		if(array_key_exists($name, $this->ticks)) {
204
			unset($this->ticks[$name]);
205
		}
206
	}
207
 
208
	/**
209
	 * Hide all ticks
210
	 *
211
	 * @param bool $hide Hide or not ?
212
	 */
213
	public function hideTicks($hide = TRUE) {
214
 
215
		foreach($this->ticks as $tick) {
216
			$tick->hide($hide);
217
		}
218
 
219
			}
220
 
221
	/**
222
	 * Change ticks style
223
	 *
224
	 * @param int $style Ticks style
225
	 */
226
	public function setTickStyle($style) {
227
 
228
		foreach($this->ticks as $tick) {
229
			$tick->setStyle($style);
230
		}
231
 
232
			}
233
 
234
	/**
235
	 * Change ticks interval
236
	 *
237
	 * @param int $interval Ticks interval
238
	 */
239
	public function setTickInterval($interval) {
240
 
241
		foreach($this->ticks as $tick) {
242
			$tick->setInterval($interval);
243
		}
244
 
245
			}
246
 
247
	/**
248
	 * Change number of ticks relative to others ticks
249
	 *
250
	 * @param awTick $to Change number of theses ticks
251
	 * @param awTick $from Ticks reference
252
	 * @param float $number Number of ticks by the reference
253
	 */
254
	public function setNumberByTick($to, $from, $number) {
255
		$this->ticks[$to]->setNumberByTick($this->ticks[$from], $number);
256
	}
257
 
258
	/**
259
	 * Reverse ticks style
260
	 */
261
	public function reverseTickStyle() {
262
 
263
		foreach($this->ticks as $tick) {
264
			if($tick->getStyle() === awTick::IN) {
265
				$tick->setStyle(awTick::OUT);
266
			} else if($tick->getStyle() === awTick::OUT) {
267
				$tick->setStyle(awTick::IN);
268
			}
269
		}
270
 
271
			}
272
 
273
	/**
274
	 * Change interval of labels
275
	 *
276
	 * @param int $interval Interval
277
	 */
278
	public function setLabelInterval($interval) {
279
		$this->auto(FALSE);
280
		$this->setTickInterval($interval);
281
		$this->label->setInterval($interval);
282
	}
283
 
284
	/**
285
	 * Change number of labels
286
	 *
287
	 * @param int $number Number of labels to display (can be NULL)
288
	 */
289
	public function setLabelNumber($number) {
290
		$this->auto(FALSE);
291
		$this->labelNumber = is_null($number) ? NULL : (int)$number;
292
	}
293
 
294
	/**
295
	 * Get number of labels
296
	 *
297
	 * @return int
298
	 */
299
	public function getLabelNumber() {
300
		return $this->labelNumber;
301
	}
302
 
303
	/**
304
	 * Change precision of labels
305
	 *
306
	 * @param int $precision Precision
307
	 */
308
	public function setLabelPrecision($precision) {
309
		$this->auto(FALSE);
310
		$function = 'axis'.time().'_'.(microtime() * 1000000);
311
		eval('function '.$function.'($value) {
312
			return sprintf("%.'.(int)$precision.'f", $value);
313
		}');
314
		$this->label->setCallbackFunction($function);
315
	}
316
 
317
	/**
318
	 * Change text of labels
319
	 *
320
	 * @param array $texts Some texts
321
	 */
322
	public function setLabelText($texts) {
323
		if(is_array($texts)) {
324
			$this->auto(FALSE);
325
			$function = 'axis'.time().'_'.(microtime() * 1000000);
326
			eval('function '.$function.'($value) {
327
				$texts = '.var_export($texts, TRUE).';
328
				return isset($texts[$value]) ? $texts[$value] : \'?\';
329
			}');
330
			$this->label->setCallbackFunction($function);
331
		}
332
	}
333
 
334
	/**
335
	 * Get the position of a point
336
	 *
337
	 * @param awAxis $xAxis X axis
338
	 * @param awAxis $yAxis Y axis
339
	 * @param awPoint $p Position of the point
340
	 * @return Point Position on the axis
341
	 */
342
	public static function toPosition(awAxis $xAxis, awAxis $yAxis, awPoint $p) {
343
 
344
		$p1 = $xAxis->getPointFromValue($p->x);
345
		$p2 = $yAxis->getPointFromValue($p->y);
346
 
347
		return new awPoint(
348
			round($p1->x),
349
			round($p2->y)
350
		);
351
 
352
	}
353
 
354
	/**
355
	 * Change title alignment
356
	 *
357
	 * @param int $alignment New Alignment
358
	 */
359
	public function setTitleAlignment($alignment) {
360
 
361
		switch($alignment) {
362
 
363
			case awLabel::TOP :
364
				$this->setTitlePosition(1);
365
				$this->title->setAlign(NULL, awLabel::BOTTOM);
366
				break;
367
 
368
			case awLabel::BOTTOM :
369
				$this->setTitlePosition(0);
370
				$this->title->setAlign(NULL, awLabel::TOP);
371
				break;
372
 
373
			case awLabel::LEFT :
374
				$this->setTitlePosition(0);
375
				$this->title->setAlign(awLabel::LEFT);
376
				break;
377
 
378
			case awLabel::RIGHT :
379
				$this->setTitlePosition(1);
380
				$this->title->setAlign(awLabel::RIGHT);
381
				break;
382
 
383
		}
384
 
385
	}
386
 
387
	/**
388
	 * Change title position on the axis
389
	 *
390
	 * @param float $position A new awposition between 0 and 1
391
	 */
392
	public function setTitlePosition($position) {
393
		$this->titlePosition = (float)$position;
394
	}
395
 
396
	/**
397
	 * Change axis and axis title color
398
	 *
399
	 * @param awColor $color
400
	 */
401
	public function setColor(awColor $color) {
402
		$this->color = $color;
403
		$this->title->setColor($color);
404
	}
405
 
406
	/**
407
	 * Change axis padding
408
	 *
409
	 * @param int $left Left padding in pixels
410
	 * @param int $right Right padding in pixels
411
	 */
412
	public function setPadding($left, $right) {
413
		$this->padding->set($left, $right);
414
	}
415
 
416
	/**
417
	 * Get axis padding
418
	 *
419
	 * @return Side
420
	 */
421
	public function getPadding() {
422
		return $this->padding;
423
	}
424
 
425
	/**
426
	 * Change axis range
427
	 *
428
	 * @param float $min
429
	 * @param float $max
430
	 */
431
	public function setRange($min, $max) {
432
		if($min !== NULL) {
433
			$this->range[0] = (float)$min;
434
		}
435
		if($max !== NULL) {
436
			$this->range[1] = (float)$max;
437
		}
438
	}
439
 
440
	/**
441
	 * Get axis range
442
	 *
443
	 * @return array
444
	 */
445
	public function getRange() {
446
		return $this->range;
447
	}
448
 
449
	/**
450
	 * Change axis range callback function
451
	 *
452
	 * @param string $toValue Transform a position between 0 and 1 to a value
453
	 * @param string $toPosition Transform a value to a position between 0 and 1 on the axis
454
	 */
455
	public function setRangeCallback($toValue, $toPosition) {
456
		$this->rangeCallback = array(
457
			'toValue' => (string)$toValue,
458
			'toPosition' => (string)$toPosition
459
		);
460
	}
461
 
462
	/**
463
	 * Center X values of the axis
464
	 *
465
	 * @param awAxis $axis An axis
466
	 * @param float $value The reference value on the axis
467
	 */
468
	public function setXCenter(awAxis $axis, $value) {
469
 
470
		// Check vector angle
471
		if($this->line->isVertical() === FALSE) {
472
			awImage::drawError("Class Axis: setXCenter() can only be used on vertical axes.");
473
		}
474
 
475
		$p = $axis->getPointFromValue($value);
476
 
477
		$this->line->setX(
478
			$p->x,
479
			$p->x
480
		);
481
 
482
	}
483
 
484
	/**
485
	 * Center Y values of the axis
486
	 *
487
	 * @param awAxis $axis An axis
488
	 * @param float $value The reference value on the axis
489
	 */
490
	public function setYCenter(awAxis $axis, $value) {
491
 
492
		// Check vector angle
493
		if($this->line->isHorizontal() === FALSE) {
494
			awImage::drawError("Class Axis: setYCenter() can only be used on horizontal axes.");
495
		}
496
 
497
		$p = $axis->getPointFromValue($value);
498
 
499
		$this->line->setY(
500
			$p->y,
501
			$p->y
502
		);
503
 
504
	}
505
 
506
	/**
507
	 * Get the distance between to values on the axis
508
	 *
509
	 * @param float $from The first value
510
	 * @param float $to The last value
511
	 * @return Point
512
	 */
513
	public function getDistance($from, $to) {
514
 
515
		$p1 = $this->getPointFromValue($from);
516
		$p2 = $this->getPointFromValue($to);
517
 
518
		return $p1->getDistance($p2);
519
 
520
	}
521
 
522
	/**
523
	 * Get a point on the axis from a value
524
	 *
525
	 * @param float $value
526
	 * @return Point
527
	 */
528
	protected function getPointFromValue($value) {
529
 
530
		$callback = $this->rangeCallback['toPosition'];
531
 
532
		list($min, $max) = $this->range;
533
		$position = $callback($value, $min, $max);
534
 
535
		return $this->getPointFromPosition($position);
536
 
537
	}
538
 
539
	/**
540
	 * Get a point on the axis from a position
541
	 *
542
	 * @param float $position A position between 0 and 1
543
	 * @return Point
544
	 */
545
	protected function getPointFromPosition($position) {
546
 
547
		$vector = $this->getVector();
548
 
549
		$angle = $vector->getAngle();
550
		$size = $vector->getSize();
551
 
552
		return $vector->p1->move(
553
			cos($angle) * $size * $position,
554
			-1 * sin($angle) * $size * $position
555
		);
556
 
557
	}
558
 
559
	/**
560
	 * Draw axis
561
	 *
562
	 * @param awDriver $driver A driver
563
	 */
564
	public function draw(awDriver $driver) {
565
 
566
		if($this->hide) {
567
			return;
568
		}
569
 
570
		$vector = $this->getVector();
571
 
572
		// Draw axis ticks
573
		$this->drawTicks($driver, $vector);
574
 
575
		// Draw axis line
576
		$this->line($driver);
577
 
578
		// Draw labels
579
		$this->drawLabels($driver);
580
 
581
		// Draw axis title
582
		$p = $this->getPointFromPosition($this->titlePosition);
583
		$this->title->draw($driver, $p);
584
 
585
	}
586
 
587
	public function autoScale() {
588
 
589
		if($this->isAuto() === FALSE) {
590
			return;
591
		}
592
 
593
		list($min, $max) = $this->getRange();
594
		$interval = $max - $min;
595
 
596
		if($interval > 0) {
597
			$partMax = $max / $interval;
598
			$partMin = $min / $interval;
599
		} else {
600
			$partMax = 0;
601
			$partMin = 0;
602
		}
603
 
604
		$difference = log($interval) / log(10);
605
		$difference = floor($difference);
606
 
607
		$pow = pow(10, $difference);
608
 
609
		if($pow > 0) {
610
			$intervalNormalize = $interval / $pow;
611
		} else {
612
			$intervalNormalize = 0;
613
		}
614
 
615
		if($difference <= 0) {
616
 
617
			$precision = $difference * -1 + 1;
618
 
619
			if($intervalNormalize > 2) {
620
				$precision--;
621
			}
622
 
623
		} else {
624
			$precision = 0;
625
		}
626
 
627
		if($min != 0 and $max != 0) {
628
			$precision++;
629
		}
630
 
631
		if($this->label->getCallbackFunction() === NULL) {
632
			$this->setLabelPrecision($precision);
633
		}
634
 
635
		if($intervalNormalize <= 1.5) {
636
			$intervalReal = 1.5;
637
			$labelNumber = 4;
638
		} else if($intervalNormalize <= 2) {
639
			$intervalReal = 2;
640
			$labelNumber = 5;
641
		} else if($intervalNormalize <= 3) {
642
			$intervalReal = 3;
643
			$labelNumber = 4;
644
		} else if($intervalNormalize <= 4) {
645
			$intervalReal = 4;
646
			$labelNumber = 5;
647
		} else if($intervalNormalize <= 5) {
648
			$intervalReal = 5;
649
			$labelNumber = 6;
650
		} else if($intervalNormalize <= 8) {
651
			$intervalReal = 8;
652
			$labelNumber = 5;
653
		} else if($intervalNormalize <= 10) {
654
			$intervalReal = 10;
655
			$labelNumber = 6;
656
		}
657
 
658
		if($min == 0) {
659
 
660
			$this->setRange(
661
				$min,
662
				$intervalReal * $pow
663
			);
664
 
665
		} else if($max == 0) {
666
 
667
			$this->setRange(
668
				$intervalReal * $pow * -1,
669
 
670
			);
671
 
672
		}
673
 
674
		$this->setLabelNumber($labelNumber);
675
 
676
	}
677
 
678
	protected function line(awDriver $driver) {
679
 
680
		$driver->line(
681
			$this->color,
682
			$this->line
683
		);
684
 
685
	}
686
 
687
	protected function drawTicks(awDriver $driver, awVector $vector) {
688
 
689
		foreach($this->ticks as $tick) {
690
			$tick->setColor($this->color);
691
			$tick->draw($driver, $vector);
692
		}
693
 
694
	}
695
 
696
	protected function drawLabels($driver) {
697
 
698
		if($this->labelNumber !== NULL) {
699
			list($min, $max) = $this->range;
700
			$number = $this->labelNumber - 1;
701
			if($number < 1) {
702
				return;
703
			}
704
			$function = $this->rangeCallback['toValue'];
705
			$labels = array();
706
			for($i = 0; $i <= $number; $i++) {
707
				$labels[] = $function($i / $number, $min, $max);
708
			}
709
			$this->label->set($labels);
710
		}
711
 
712
		$labels = $this->label->count();
713
 
714
		for($i = 0; $i < $labels; $i++) {
715
 
716
			$p = $this->getPointFromValue($this->label->get($i));
717
			$this->label->draw($driver, $p, $i);
718
 
719
		}
720
 
721
	}
722
 
723
	protected function getVector() {
724
 
725
		$angle = $this->line->getAngle();
726
 
727
		// Compute paddings
728
		$vector = new awVector(
729
			$this->line->p1->move(
730
				cos($angle) * $this->padding->left,
731
				-1 * sin($angle) * $this->padding->left
732
			),
733
			$this->line->p2->move(
734
				-1 * cos($angle) * $this->padding->right,
735
				-1 * -1 * sin($angle) * $this->padding->right
736
			)
737
		);
738
 
739
		return $vector;
740
 
741
	}
742
 
743
	public function __clone() {
744
 
745
		$this->label = clone $this->label;
746
		$this->line = clone $this->line;
747
		$this->title = clone $this->title;
748
 
749
		foreach($this->ticks as $name => $tick) {
750
			$this->ticks[$name] = clone $tick;
751
		}
752
 
753
	}
754
 
755
}
756
 
757
registerClass('Axis');
758
 
759
function toProportionalValue($position, $min, $max) {
760
	return $min + ($max - $min) * $position;
761
}
762
 
763
function toProportionalPosition($value, $min, $max) {
764
	if($max - $min == 0) {
765
		return 0;
766
	}
767
	return ($value - $min) / ($max - $min);
768
}
769
?>