1 | <?php // content="text/plain; charset=utf-8" |
---|
2 | /** |
---|
3 | * Class CCBPGraph |
---|
4 | * Utility class to create Critical Chain Buffer penetration charts |
---|
5 | */ |
---|
6 | class CCBPGraph { |
---|
7 | const TickStep = 25; |
---|
8 | const YTitle = '% Buffer used'; |
---|
9 | const XTitle = '% CC Completed'; |
---|
10 | const NColorMaps = 2; |
---|
11 | private $graph=null; |
---|
12 | private $iWidth,$iHeight; |
---|
13 | private $iPlots=array(); |
---|
14 | private $iXMin=-50, $iXMax = 100; |
---|
15 | private $iYMin=-50, $iYMax = 150; |
---|
16 | private $iColorInd = array( |
---|
17 | array(5,75), /* Green */ |
---|
18 | array(25,85), /* Yellow */ |
---|
19 | array(50,100));/* Red */ |
---|
20 | private $iColorMap = 0; |
---|
21 | private $iColorSpec = array( |
---|
22 | array('darkgreen:1.0','yellow:1.4','red:0.8','darkred:0.85'), |
---|
23 | array('#c6e9af','#ffeeaa','#ffaaaa','#de8787')); |
---|
24 | private $iMarginColor = array('darkgreen@0.7','darkgreen@0.9'); |
---|
25 | private $iSubTitle='',$iTitle = 'CC Buffer penetration'; |
---|
26 | /** |
---|
27 | * Construct a new instance of CCBPGraph |
---|
28 | * |
---|
29 | * @param int $aWidth |
---|
30 | * @param int $aHeight |
---|
31 | * @return CCBPGraph |
---|
32 | */ |
---|
33 | public function __construct($aWidth, $aHeight) { |
---|
34 | $this->iWidth = $aWidth; |
---|
35 | $this->iHeight = $aHeight; |
---|
36 | } |
---|
37 | /** |
---|
38 | * Set the title and subtitle for the graph |
---|
39 | * |
---|
40 | * @param string $aTitle |
---|
41 | * @param string $aSubTitle |
---|
42 | */ |
---|
43 | public function SetTitle($aTitle, $aSubTitle) { |
---|
44 | $this->iTitle = $aTitle; |
---|
45 | $this->iSubTitle = $aSubTitle; |
---|
46 | } |
---|
47 | /** |
---|
48 | * Set the x-axis min and max values |
---|
49 | * |
---|
50 | * @param int $aMin |
---|
51 | * @param int $aMax |
---|
52 | */ |
---|
53 | public function SetXMinMax($aMin, $aMax) { |
---|
54 | $this->iXMin = floor($aMin/CCBPGraph::TickStep)*CCBPGraph::TickStep; |
---|
55 | $this->iXMax = ceil($aMax/CCBPGraph::TickStep)*CCBPGraph::TickStep; |
---|
56 | } |
---|
57 | /** |
---|
58 | * Specify what color map to use |
---|
59 | * |
---|
60 | * @param int $aMap |
---|
61 | */ |
---|
62 | public function SetColorMap($aMap) { |
---|
63 | $this->iColorMap = $aMap % CCBPGraph::NColorMaps; |
---|
64 | } |
---|
65 | /** |
---|
66 | * Set the y-axis min and max values |
---|
67 | * |
---|
68 | * @param int $aMin |
---|
69 | * @param int $aMax |
---|
70 | */ |
---|
71 | public function SetYMinMax($aMin,$aMax) { |
---|
72 | $this->iYMin = floor($aMin/CCBPGraph::TickStep)*CCBPGraph::TickStep; |
---|
73 | $this->iYMax = ceil($aMax/CCBPGraph::TickStep)*CCBPGraph::TickStep; |
---|
74 | } |
---|
75 | /** |
---|
76 | * Set the specification of the color backgrounds and also the |
---|
77 | * optional exact colors to be used |
---|
78 | * |
---|
79 | * @param mixed $aSpec An array of 3 1x2 arrays. Each array specify the |
---|
80 | * color indication value at x=0 and x=max x in order to determine the slope |
---|
81 | * @param mixed $aColors An array with four elements specifying the colors |
---|
82 | * of each color indicator |
---|
83 | */ |
---|
84 | public function SetColorIndication(array $aSpec,array $aColors=null) { |
---|
85 | if( count($aSpec) !== 3 ) { |
---|
86 | JpgraphError::Raise('Specification of scale values for background indicators must be an array with three elements.'); |
---|
87 | } |
---|
88 | $this->iColorInd = $aSpec; |
---|
89 | if( $aColors !== null ) { |
---|
90 | if( is_array($aColors) && count($aColors) == 4 ) { |
---|
91 | $this->iColorSpec = $aColors; |
---|
92 | } |
---|
93 | else { |
---|
94 | JpGraphError::Raise('Color specification for background indication must have four colors.'); |
---|
95 | } |
---|
96 | } |
---|
97 | } |
---|
98 | /** |
---|
99 | * Construct the graph |
---|
100 | * |
---|
101 | */ |
---|
102 | private function Init() { |
---|
103 | |
---|
104 | // Setup limits for color indications |
---|
105 | $lowx = $this->iXMin; $highx= $this->iXMax; |
---|
106 | $lowy = $this->iYMin; $highy = $this->iYMax; |
---|
107 | $width=$this->iWidth; $height=$this->iHeight; |
---|
108 | |
---|
109 | // Margins |
---|
110 | $lm=50; $rm=40; |
---|
111 | $tm=60; $bm=40; |
---|
112 | |
---|
113 | if( $width <= 300 || $height <= 250 ) { |
---|
114 | $labelsize = 8; |
---|
115 | $lm=25; $rm=25; |
---|
116 | $tm=45; $bm=25; |
---|
117 | } |
---|
118 | elseif( $width <= 450 || $height <= 300 ) { |
---|
119 | $labelsize = 8; |
---|
120 | $lm=30; $rm=30; |
---|
121 | $tm=50; $bm=30; |
---|
122 | } |
---|
123 | elseif( $width <= 600 || $height <= 400 ) { |
---|
124 | $labelsize = 9; |
---|
125 | } |
---|
126 | else { |
---|
127 | $labelsize = 11; |
---|
128 | } |
---|
129 | |
---|
130 | if( $this->iSubTitle == '' ) { |
---|
131 | $tm -= $labelsize+4; |
---|
132 | } |
---|
133 | |
---|
134 | $graph = new Graph($width,$height); |
---|
135 | $graph->SetScale('intint',$lowy,$highy,$lowx,$highx); |
---|
136 | $graph->SetMargin($lm,$rm,$tm,$bm); |
---|
137 | $graph->SetMarginColor($this->iMarginColor[$this->iColorMap]); |
---|
138 | $graph->SetClipping(); |
---|
139 | |
---|
140 | $graph->title->Set($this->iTitle); |
---|
141 | $graph->subtitle->Set($this->iSubTitle); |
---|
142 | |
---|
143 | $graph->title->SetFont(FF_ARIAL,FS_BOLD,$labelsize+4); |
---|
144 | $graph->subtitle->SetFont(FF_ARIAL,FS_BOLD,$labelsize+1); |
---|
145 | |
---|
146 | $graph->SetBox(true,'black@0.3'); |
---|
147 | |
---|
148 | $graph->xaxis->SetFont(FF_ARIAL,FS_BOLD,$labelsize); |
---|
149 | $graph->yaxis->SetFont(FF_ARIAL,FS_BOLD,$labelsize); |
---|
150 | |
---|
151 | $graph->xaxis->scale->ticks->Set(CCBPGraph::TickStep,CCBPGraph::TickStep); |
---|
152 | $graph->yaxis->scale->ticks->Set(CCBPGraph::TickStep,CCBPGraph::TickStep); |
---|
153 | |
---|
154 | $graph->xaxis->HideZeroLabel(); |
---|
155 | $graph->yaxis->HideZeroLabel(); |
---|
156 | |
---|
157 | $graph->xaxis->SetLabelFormatString('%d%%'); |
---|
158 | $graph->yaxis->SetLabelFormatString('%d%%'); |
---|
159 | |
---|
160 | // For the x-axis we adjust the color so labels on the left of the Y-axis are in black |
---|
161 | $n1 = floor(abs($this->iXMin/25))+1; |
---|
162 | $n2 = floor($this->iXMax/25); |
---|
163 | if( $this->iColorMap == 0 ) { |
---|
164 | $xlcolors=array(); |
---|
165 | for( $i = 0; $i < $n1; ++$i ) { |
---|
166 | $xlcolors[$i] = 'black'; |
---|
167 | } |
---|
168 | for( $i = 0; $i < $n2; ++$i ) { |
---|
169 | $xlcolors[$n1+$i] = 'lightgray:1.5'; |
---|
170 | } |
---|
171 | $graph->xaxis->SetColor('gray',$xlcolors); |
---|
172 | $graph->yaxis->SetColor('gray','lightgray:1.5'); |
---|
173 | } |
---|
174 | else { |
---|
175 | $graph->xaxis->SetColor('darkgray','darkgray:0.8'); |
---|
176 | $graph->yaxis->SetColor('darkgray','darkgray:0.8'); |
---|
177 | } |
---|
178 | $graph->SetGridDepth(DEPTH_FRONT); |
---|
179 | $graph->ygrid->SetColor('gray@0.6'); |
---|
180 | $graph->ygrid->SetLineStyle('dotted'); |
---|
181 | |
---|
182 | $graph->ygrid->Show(); |
---|
183 | |
---|
184 | $graph->xaxis->SetWeight(1); |
---|
185 | $graph->yaxis->SetWeight(1); |
---|
186 | |
---|
187 | $ytitle = new Text(CCBPGraph::YTitle,floor($lm*.75),($height-$tm-$bm)/2+$tm); |
---|
188 | #$ytitle->SetFont(FF_VERA,FS_BOLD,$labelsize+1); |
---|
189 | $ytitle->SetAlign('right','center'); |
---|
190 | $ytitle->SetAngle(90); |
---|
191 | $graph->Add($ytitle); |
---|
192 | |
---|
193 | $xtitle = new Text(CCBPGraph::XTitle,($width-$lm-$rm)/2+$lm,$height - 10); |
---|
194 | #$xtitle->SetFont(FF_VERA,FS_BOLD,$labelsize); |
---|
195 | $xtitle->SetAlign('center','bottom'); |
---|
196 | $graph->Add($xtitle); |
---|
197 | |
---|
198 | $df = 'D j:S M, Y'; |
---|
199 | if( $width < 400 ) { |
---|
200 | $df = 'D j:S M'; |
---|
201 | } |
---|
202 | |
---|
203 | $time = new Text(date($df),$width-10,$height-10); |
---|
204 | $time->SetAlign('right','bottom'); |
---|
205 | #$time->SetFont(FF_VERA,FS_NORMAL,$labelsize-1); |
---|
206 | $time->SetColor('darkgray'); |
---|
207 | $graph->Add($time); |
---|
208 | |
---|
209 | // Use an accumulated fille line graph to create the colored bands |
---|
210 | |
---|
211 | $n = 3; |
---|
212 | for( $i=0; $i < $n; ++$i ) { |
---|
213 | $b = $this->iColorInd[$i][0]; |
---|
214 | $k = ($this->iColorInd[$i][1] - $this->iColorInd[$i][0])/$this->iXMax; |
---|
215 | $colarea[$i] = array( array($lowx,$lowx*$k+$b), array($highx,$highx*$k+$b) ); |
---|
216 | } |
---|
217 | $colarea[3] = array( array($lowx,$highy), array($highx,$highy) ); |
---|
218 | |
---|
219 | |
---|
220 | $cb = array(); |
---|
221 | for( $i=0; $i < 4; ++$i ) { |
---|
222 | $cb[$i] = new LinePlot(array($colarea[$i][0][1],$colarea[$i][1][1]), |
---|
223 | array($colarea[$i][0][0],$colarea[$i][1][0])); |
---|
224 | $cb[$i]->SetFillColor($this->iColorSpec[$this->iColorMap][$i]); |
---|
225 | $cb[$i]->SetFillFromYMin(); |
---|
226 | } |
---|
227 | |
---|
228 | $graph->Add(array_slice(array_reverse($cb),0,4)); |
---|
229 | $this->graph = $graph; |
---|
230 | } |
---|
231 | /** |
---|
232 | * Add a line or scatter plot to the graph |
---|
233 | * |
---|
234 | * @param mixed $aPlots |
---|
235 | */ |
---|
236 | public function Add($aPlots) { |
---|
237 | if( is_array($aPlots) ) { |
---|
238 | $this->iPlots = array_merge($this->iPlots,$aPlots); |
---|
239 | } |
---|
240 | else { |
---|
241 | $this->iPlots[] = $aPlots; |
---|
242 | } |
---|
243 | } |
---|
244 | /** |
---|
245 | * Stroke the graph back to the client or to a file |
---|
246 | * |
---|
247 | * @param mixed $aFile |
---|
248 | */ |
---|
249 | public function Stroke($aFile='') { |
---|
250 | $this->Init(); |
---|
251 | if( count($this->iPlots) > 0 ) { |
---|
252 | $this->graph->Add($this->iPlots); |
---|
253 | } |
---|
254 | $this->graph->Stroke($aFile); |
---|
255 | } |
---|
256 | } |
---|
257 | ?> |
---|