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