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