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
|
|
|
|