1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* JPGraph v4.0.3 |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace Amenadiel\JpGraph\Plot; |
8
|
|
|
|
9
|
|
|
use Amenadiel\JpGraph\Text; |
10
|
|
|
use Amenadiel\JpGraph\Util; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* File: JPGRAPH_PIE3D.PHP |
14
|
|
|
* // Description: 3D Pie plot extension for JpGraph |
15
|
|
|
* // Created: 2001-03-24 |
16
|
|
|
* // Ver: $Id: jpgraph_pie3d.php 1329 2009-06-20 19:23:30Z ljp $ |
17
|
|
|
* // |
18
|
|
|
* // Copyright (c) Asial Corporation. All rights reserved. |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @class PiePlot3D |
23
|
|
|
* // Description: Plots a 3D pie with a specified projection |
24
|
|
|
* // angle between 20 and 70 degrees. |
25
|
|
|
*/ |
26
|
|
|
class PiePlot3D extends PiePlot |
27
|
|
|
{ |
28
|
|
|
private $labelhintcolor = 'red'; |
29
|
|
|
private $showlabelhint = true; |
30
|
|
|
private $angle = 50; |
31
|
|
|
private $edgecolor = ''; |
32
|
|
|
private $edgeweight = 1; |
33
|
|
|
private $iThickness = false; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* CONSTRUCTOR. |
37
|
|
|
* |
38
|
|
|
* @param mixed $data |
39
|
|
|
*/ |
40
|
2 |
|
public function __construct($data) |
41
|
|
|
{ |
42
|
2 |
|
$this->radius = 0.5; |
43
|
2 |
|
$this->data = $data; |
44
|
2 |
|
$this->title = new Text\Text(''); |
45
|
2 |
|
$this->title->SetFont(FF_FONT1, FS_BOLD); |
46
|
2 |
|
$this->value = new DisplayValue(); |
47
|
2 |
|
$this->value->Show(); |
48
|
2 |
|
$this->value->SetFormat('%.0f%%'); |
49
|
2 |
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* PUBLIC METHODS. |
53
|
|
|
* |
54
|
|
|
* @param mixed $aLegend |
55
|
|
|
*/ |
56
|
|
|
// Set label arrays |
57
|
2 |
|
public function SetLegends($aLegend) |
58
|
|
|
{ |
59
|
2 |
|
$this->legends = array_reverse(array_slice($aLegend, 0, safe_count($this->data))); |
60
|
2 |
|
} |
61
|
|
|
|
62
|
|
|
public function SetSliceColors($aColors) |
63
|
|
|
{ |
64
|
|
|
$this->setslicecolors = $aColors; |
65
|
|
|
} |
66
|
|
|
|
67
|
2 |
|
public function Legend($aGraph) |
68
|
|
|
{ |
69
|
2 |
|
parent::Legend($aGraph); |
70
|
2 |
|
$aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol); |
71
|
2 |
|
} |
72
|
|
|
|
73
|
|
|
public function SetCSIMTargets($aTargets, $aAlts = '', $aWinTargets = '') |
74
|
|
|
{ |
75
|
|
|
$this->csimtargets = $aTargets; |
76
|
|
|
$this->csimwintargets = $aWinTargets; |
77
|
|
|
$this->csimalts = $aAlts; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
// Should the slices be separated by a line? If color is specified as "" no line |
81
|
|
|
// will be used to separate pie slices. |
82
|
1 |
|
public function SetEdge($aColor = 'black', $aWeight = 1) |
83
|
|
|
{ |
84
|
1 |
|
$this->edgecolor = $aColor; |
85
|
1 |
|
$this->edgeweight = $aWeight; |
86
|
1 |
|
} |
87
|
|
|
|
88
|
|
|
// Specify projection angle for 3D in degrees |
89
|
|
|
// Must be between 20 and 70 degrees |
90
|
2 |
|
public function SetAngle($a) |
91
|
|
|
{ |
92
|
2 |
|
if ($a < 5 || $a > 90) { |
93
|
|
|
Util\JpGraphError::RaiseL(14002); |
94
|
|
|
//("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees."); |
95
|
|
|
} else { |
96
|
2 |
|
$this->angle = $a; |
97
|
|
|
} |
98
|
2 |
|
} |
99
|
|
|
|
100
|
2 |
|
public function Add3DSliceToCSIM($i, $xc, $yc, $height, $width, $thick, $sa, $ea) |
101
|
|
|
{ |
102
|
|
|
//Slice number, ellipse centre (x,y), height, width, start angle, end angle |
103
|
|
|
|
104
|
2 |
|
$sa *= M_PI / 180; |
105
|
2 |
|
$ea *= M_PI / 180; |
106
|
|
|
|
107
|
|
|
//add coordinates of the centre to the map |
108
|
2 |
|
$coords = "${xc}, ${yc}"; |
109
|
|
|
|
110
|
|
|
//add coordinates of the first point on the arc to the map |
111
|
2 |
|
$xp = floor($width * cos($sa) / 2 + $xc); |
112
|
2 |
|
$yp = floor($yc - $height * sin($sa) / 2); |
113
|
2 |
|
$coords .= ", ${xp}, ${yp}"; |
114
|
|
|
|
115
|
|
|
//If on the front half, add the thickness offset |
116
|
2 |
|
if ($sa >= M_PI && $sa <= 2 * M_PI * 1.01) { |
117
|
2 |
|
$yp = floor($yp + $thick); |
118
|
2 |
|
$coords .= ", ${xp}, ${yp}"; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
//add coordinates every 0.2 radians |
122
|
2 |
|
$a = $sa + 0.2; |
123
|
2 |
|
while ($a < $ea) { |
124
|
2 |
|
$xp = floor($width * cos($a) / 2 + $xc); |
125
|
2 |
|
if ($a >= M_PI && $a <= 2 * M_PI * 1.01) { |
126
|
2 |
|
$yp = floor($yc - ($height * sin($a) / 2) + $thick); |
127
|
|
|
} else { |
128
|
2 |
|
$yp = floor($yc - $height * sin($a) / 2); |
129
|
|
|
} |
130
|
2 |
|
$coords .= ", ${xp}, ${yp}"; |
131
|
2 |
|
$a += 0.2; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
//Add the last point on the arc |
135
|
2 |
|
$xp = floor($width * cos($ea) / 2 + $xc); |
136
|
2 |
|
$yp = floor($yc - $height * sin($ea) / 2); |
137
|
|
|
|
138
|
2 |
|
if ($ea >= M_PI && $ea <= 2 * M_PI * 1.01) { |
139
|
2 |
|
$coords .= ", ${xp}, " . floor($yp + $thick); |
140
|
|
|
} |
141
|
2 |
|
$coords .= ", ${xp}, ${yp}"; |
142
|
2 |
|
$alt = ''; |
|
|
|
|
143
|
|
|
|
144
|
2 |
|
if (!empty($this->csimtargets[$i])) { |
145
|
|
|
$this->csimareas .= "<area shape=\"poly\" coords=\"${coords}\" href=\"" . $this->csimtargets[$i] . '"'; |
146
|
|
|
|
147
|
|
|
if (!empty($this->csimwintargets[$i])) { |
148
|
|
|
$this->csimareas .= ' target="' . $this->csimwintargets[$i] . '" '; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
if (!empty($this->csimalts[$i])) { |
152
|
|
|
$tmp = sprintf($this->csimalts[$i], $this->data[$i]); |
153
|
|
|
$this->csimareas .= "alt=\"${tmp}\" title=\"${tmp}\" "; |
154
|
|
|
} |
155
|
|
|
$this->csimareas .= " />\n"; |
156
|
|
|
} |
157
|
2 |
|
} |
158
|
|
|
|
159
|
|
|
public function SetLabels($aLabels, $aLblPosAdj = 'auto') |
160
|
|
|
{ |
161
|
|
|
$this->labels = $aLabels; |
162
|
|
|
$this->ilabelposadj = $aLblPosAdj; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
// Distance from the pie to the labels |
166
|
|
|
public function SetLabelMargin($m) |
167
|
|
|
{ |
168
|
|
|
$this->value->SetMargin($m); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
// Show a thin line from the pie to the label for a specific slice |
172
|
|
|
public function ShowLabelHint($f = true) |
173
|
|
|
{ |
174
|
|
|
$this->showlabelhint = $f; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
// Set color of hint line to label for each slice |
178
|
|
|
public function SetLabelHintColor($c) |
179
|
|
|
{ |
180
|
|
|
$this->labelhintcolor = $c; |
181
|
|
|
} |
182
|
|
|
|
183
|
1 |
|
public function SetHeight($aHeight) |
184
|
|
|
{ |
185
|
1 |
|
$this->iThickness = $aHeight; |
186
|
1 |
|
} |
187
|
|
|
|
188
|
|
|
// Normalize Angle between 0-360 |
189
|
2 |
|
public function NormAngle($a) |
190
|
|
|
{ |
191
|
|
|
// Normalize anle to 0 to 2M_PI |
192
|
|
|
// |
193
|
2 |
|
if ($a > 0) { |
194
|
2 |
|
while ($a > 360) { |
195
|
1 |
|
$a -= 360; |
196
|
|
|
} |
197
|
|
|
} else { |
198
|
2 |
|
while ($a < 0) { |
199
|
|
|
$a += 360; |
200
|
|
|
} |
201
|
|
|
} |
202
|
2 |
|
if ($a < 0) { |
203
|
|
|
$a = 360 + $a; |
204
|
|
|
} |
205
|
|
|
|
206
|
2 |
|
if ($a == 360) { |
207
|
2 |
|
$a = 0; |
208
|
|
|
} |
209
|
|
|
|
210
|
2 |
|
return $a; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
// Draw one 3D pie slice at position ($xc,$yc) with height $z |
214
|
2 |
|
public function Pie3DSlice($img, $xc, $yc, $w, $h, $sa, $ea, $z, $fillcolor, $shadow = 0.65) |
215
|
|
|
{ |
216
|
|
|
// Due to the way the 3D Pie algorithm works we are |
217
|
|
|
// guaranteed that any slice we get into this method |
218
|
|
|
// belongs to either the left or right side of the |
219
|
|
|
// pie ellipse. Hence, no slice will cross 90 or 270 |
220
|
|
|
// point. |
221
|
2 |
|
if (($sa < 90 && $ea > 90) || (($sa > 90 && $sa < 270) && $ea > 270)) { |
222
|
|
|
Util\JpGraphError::RaiseL(14003); //('Internal assertion failed. Pie3D::Pie3DSlice'); |
223
|
|
|
exit(1); |
|
|
|
|
224
|
|
|
} |
225
|
|
|
|
226
|
2 |
|
$p[] = []; |
|
|
|
|
227
|
|
|
|
228
|
|
|
// Setup pre-calculated values |
229
|
2 |
|
$rsa = $sa / 180 * M_PI; // to Rad |
230
|
2 |
|
$rea = $ea / 180 * M_PI; // to Rad |
231
|
2 |
|
$sinsa = sin($rsa); |
232
|
2 |
|
$cossa = cos($rsa); |
233
|
2 |
|
$sinea = sin($rea); |
234
|
2 |
|
$cosea = cos($rea); |
235
|
|
|
|
236
|
|
|
// p[] is the points for the overall slice and |
237
|
|
|
// pt[] is the points for the top pie |
238
|
|
|
|
239
|
|
|
// Angular step when approximating the arc with a polygon train. |
240
|
2 |
|
$step = 0.05; |
241
|
|
|
|
242
|
2 |
|
if ($sa >= 270) { |
243
|
2 |
|
if ($ea > 360 || ($ea > 0 && $ea <= 90)) { |
244
|
1 |
|
if ($ea > 0 && $ea <= 90) { |
245
|
|
|
// Adjust angle to simplify conditions in loops |
246
|
1 |
|
$rea += 2 * M_PI; |
247
|
|
|
} |
248
|
|
|
|
249
|
1 |
|
$p = [$xc, $yc, $xc, $yc + $z, |
250
|
1 |
|
$xc + $w * $cossa, $z + $yc - $h * $sinsa, ]; |
251
|
1 |
|
$pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa]; |
252
|
|
|
|
253
|
1 |
|
for ($a = $rsa; $a < 2 * M_PI; $a += $step) { |
254
|
1 |
|
$tca = cos($a); |
255
|
1 |
|
$tsa = sin($a); |
256
|
1 |
|
$p[] = $xc + $w * $tca; |
257
|
1 |
|
$p[] = $z + $yc - $h * $tsa; |
258
|
1 |
|
$pt[] = $xc + $w * $tca; |
259
|
1 |
|
$pt[] = $yc - $h * $tsa; |
260
|
|
|
} |
261
|
|
|
|
262
|
1 |
|
$pt[] = $xc + $w; |
263
|
1 |
|
$pt[] = $yc; |
264
|
|
|
|
265
|
1 |
|
$p[] = $xc + $w; |
266
|
1 |
|
$p[] = $z + $yc; |
267
|
1 |
|
$p[] = $xc + $w; |
268
|
1 |
|
$p[] = $yc; |
269
|
1 |
|
$p[] = $xc; |
270
|
1 |
|
$p[] = $yc; |
271
|
|
|
|
272
|
1 |
|
for ($a = 2 * M_PI + $step; $a < $rea; $a += $step) { |
273
|
1 |
|
$pt[] = $xc + $w * cos($a); |
274
|
1 |
|
$pt[] = $yc - $h * sin($a); |
275
|
|
|
} |
276
|
|
|
|
277
|
1 |
|
$pt[] = $xc + $w * $cosea; |
278
|
1 |
|
$pt[] = $yc - $h * $sinea; |
279
|
1 |
|
$pt[] = $xc; |
280
|
1 |
|
$pt[] = $yc; |
281
|
|
|
} else { |
282
|
2 |
|
$p = [$xc, $yc, $xc, $yc + $z, |
283
|
2 |
|
$xc + $w * $cossa, $z + $yc - $h * $sinsa, ]; |
284
|
2 |
|
$pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa]; |
285
|
|
|
|
286
|
2 |
|
$rea = $rea == 0.0 ? 2 * M_PI : $rea; |
287
|
2 |
|
for ($a = $rsa; $a < $rea; $a += $step) { |
288
|
2 |
|
$tca = cos($a); |
289
|
2 |
|
$tsa = sin($a); |
290
|
2 |
|
$p[] = $xc + $w * $tca; |
291
|
2 |
|
$p[] = $z + $yc - $h * $tsa; |
292
|
2 |
|
$pt[] = $xc + $w * $tca; |
293
|
2 |
|
$pt[] = $yc - $h * $tsa; |
294
|
|
|
} |
295
|
|
|
|
296
|
2 |
|
$pt[] = $xc + $w * $cosea; |
297
|
2 |
|
$pt[] = $yc - $h * $sinea; |
298
|
2 |
|
$pt[] = $xc; |
299
|
2 |
|
$pt[] = $yc; |
300
|
|
|
|
301
|
2 |
|
$p[] = $xc + $w * $cosea; |
302
|
2 |
|
$p[] = $z + $yc - $h * $sinea; |
303
|
2 |
|
$p[] = $xc + $w * $cosea; |
304
|
2 |
|
$p[] = $yc - $h * $sinea; |
305
|
2 |
|
$p[] = $xc; |
306
|
2 |
|
$p[] = $yc; |
307
|
|
|
} |
308
|
2 |
|
} elseif ($sa >= 180) { |
309
|
2 |
|
$p = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea]; |
310
|
2 |
|
$pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea]; |
311
|
|
|
|
312
|
2 |
|
for ($a = $rea; $a > $rsa; $a -= $step) { |
313
|
2 |
|
$tca = cos($a); |
314
|
2 |
|
$tsa = sin($a); |
315
|
2 |
|
$p[] = $xc + $w * $tca; |
316
|
2 |
|
$p[] = $z + $yc - $h * $tsa; |
317
|
2 |
|
$pt[] = $xc + $w * $tca; |
318
|
2 |
|
$pt[] = $yc - $h * $tsa; |
319
|
|
|
} |
320
|
|
|
|
321
|
2 |
|
$pt[] = $xc + $w * $cossa; |
322
|
2 |
|
$pt[] = $yc - $h * $sinsa; |
323
|
2 |
|
$pt[] = $xc; |
324
|
2 |
|
$pt[] = $yc; |
325
|
|
|
|
326
|
2 |
|
$p[] = $xc + $w * $cossa; |
327
|
2 |
|
$p[] = $z + $yc - $h * $sinsa; |
328
|
2 |
|
$p[] = $xc + $w * $cossa; |
329
|
2 |
|
$p[] = $yc - $h * $sinsa; |
330
|
2 |
|
$p[] = $xc; |
331
|
2 |
|
$p[] = $yc; |
332
|
2 |
|
} elseif ($sa >= 90) { |
333
|
2 |
|
if ($ea > 180) { |
334
|
2 |
|
$p = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea]; |
335
|
2 |
|
$pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea]; |
336
|
|
|
|
337
|
2 |
|
for ($a = $rea; $a > M_PI; $a -= $step) { |
338
|
2 |
|
$tca = cos($a); |
339
|
2 |
|
$tsa = sin($a); |
340
|
2 |
|
$p[] = $xc + $w * $tca; |
341
|
2 |
|
$p[] = $z + $yc - $h * $tsa; |
342
|
2 |
|
$pt[] = $xc + $w * $tca; |
343
|
2 |
|
$pt[] = $yc - $h * $tsa; |
344
|
|
|
} |
345
|
|
|
|
346
|
2 |
|
$p[] = $xc - $w; |
347
|
2 |
|
$p[] = $z + $yc; |
348
|
2 |
|
$p[] = $xc - $w; |
349
|
2 |
|
$p[] = $yc; |
350
|
2 |
|
$p[] = $xc; |
351
|
2 |
|
$p[] = $yc; |
352
|
|
|
|
353
|
2 |
|
$pt[] = $xc - $w; |
354
|
2 |
|
$pt[] = $z + $yc; |
355
|
2 |
|
$pt[] = $xc - $w; |
356
|
2 |
|
$pt[] = $yc; |
357
|
|
|
|
358
|
2 |
|
for ($a = M_PI - $step; $a > $rsa; $a -= $step) { |
359
|
2 |
|
$pt[] = $xc + $w * cos($a); |
360
|
2 |
|
$pt[] = $yc - $h * sin($a); |
361
|
|
|
} |
362
|
|
|
|
363
|
2 |
|
$pt[] = $xc + $w * $cossa; |
364
|
2 |
|
$pt[] = $yc - $h * $sinsa; |
365
|
2 |
|
$pt[] = $xc; |
366
|
2 |
|
$pt[] = $yc; |
367
|
|
|
} else { |
368
|
|
|
// $sa >= 90 && $ea <= 180 |
369
|
2 |
|
$p = [$xc, $yc, $xc, $yc + $z, |
370
|
2 |
|
$xc + $w * $cosea, $z + $yc - $h * $sinea, |
371
|
2 |
|
$xc + $w * $cosea, $yc - $h * $sinea, |
372
|
2 |
|
$xc, $yc, ]; |
373
|
|
|
|
374
|
2 |
|
$pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea]; |
375
|
|
|
|
376
|
2 |
|
for ($a = $rea; $a > $rsa; $a -= $step) { |
377
|
2 |
|
$pt[] = $xc + $w * cos($a); |
378
|
2 |
|
$pt[] = $yc - $h * sin($a); |
379
|
|
|
} |
380
|
|
|
|
381
|
2 |
|
$pt[] = $xc + $w * $cossa; |
382
|
2 |
|
$pt[] = $yc - $h * $sinsa; |
383
|
2 |
|
$pt[] = $xc; |
384
|
2 |
|
$pt[] = $yc; |
385
|
|
|
} |
386
|
|
|
} else { |
387
|
|
|
// sa > 0 && ea < 90 |
388
|
|
|
|
389
|
2 |
|
$p = [$xc, $yc, $xc, $yc + $z, |
390
|
2 |
|
$xc + $w * $cossa, $z + $yc - $h * $sinsa, |
391
|
2 |
|
$xc + $w * $cossa, $yc - $h * $sinsa, |
392
|
2 |
|
$xc, $yc, ]; |
393
|
|
|
|
394
|
2 |
|
$pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa]; |
395
|
|
|
|
396
|
2 |
|
for ($a = $rsa; $a < $rea; $a += $step) { |
397
|
2 |
|
$pt[] = $xc + $w * cos($a); |
398
|
2 |
|
$pt[] = $yc - $h * sin($a); |
399
|
|
|
} |
400
|
|
|
|
401
|
2 |
|
$pt[] = $xc + $w * $cosea; |
402
|
2 |
|
$pt[] = $yc - $h * $sinea; |
403
|
2 |
|
$pt[] = $xc; |
404
|
2 |
|
$pt[] = $yc; |
405
|
|
|
} |
406
|
|
|
|
407
|
2 |
|
$img->PushColor($fillcolor . ':' . $shadow); |
408
|
2 |
|
$img->FilledPolygon($p); |
409
|
2 |
|
$img->PopColor(); |
410
|
|
|
|
411
|
2 |
|
$img->PushColor($fillcolor); |
412
|
2 |
|
$img->FilledPolygon($pt); |
413
|
2 |
|
$img->PopColor(); |
414
|
2 |
|
} |
415
|
|
|
|
416
|
1 |
|
public function SetStartAngle($aStart) |
417
|
|
|
{ |
418
|
1 |
|
if ($aStart < 0 || $aStart > 360) { |
419
|
|
|
Util\JpGraphError::RaiseL(14004); //('Slice start angle must be between 0 and 360 degrees.'); |
420
|
|
|
} |
421
|
1 |
|
$this->startangle = $aStart; |
422
|
1 |
|
} |
423
|
|
|
|
424
|
|
|
// Draw a 3D Pie |
425
|
2 |
|
public function Pie3D( |
426
|
|
|
$aaoption, |
427
|
|
|
$img, |
428
|
|
|
$data, |
429
|
|
|
$colors, |
430
|
|
|
$xc, |
431
|
|
|
$yc, |
432
|
|
|
$d, |
433
|
|
|
$angle, |
434
|
|
|
$z, |
435
|
|
|
$shadow = 0.65, |
436
|
|
|
$startangle = 0, |
437
|
|
|
$edgecolor = '', |
438
|
|
|
$edgeweight = 1 |
439
|
|
|
) { |
440
|
|
|
/** |
441
|
|
|
* As usual the algorithm get more complicated than I originally |
442
|
|
|
* // envisioned. I believe that this is as simple as it is possible |
443
|
|
|
* // to do it with the features I want. It's a good exercise to start |
444
|
|
|
* // thinking on how to do this to convince your self that all this |
445
|
|
|
* // is really needed for the general case. |
446
|
|
|
* // |
447
|
|
|
* // The algorithm two draw 3D pies without "real 3D" is done in |
448
|
|
|
* // two steps. |
449
|
|
|
* // First imagine the pie cut in half through a thought line between |
450
|
|
|
* // 12'a clock and 6'a clock. It now easy to imagine that we can plot |
451
|
|
|
* // the individual slices for each half by starting with the topmost |
452
|
|
|
* // pie slice and continue down to 6'a clock. |
453
|
|
|
* // |
454
|
|
|
* // In the algortithm this is done in three principal steps |
455
|
|
|
* // Step 1. Do the knife cut to ensure by splitting slices that extends |
456
|
|
|
* // over the cut line. This is done by splitting the original slices into |
457
|
|
|
* // upto 3 subslices. |
458
|
|
|
* // Step 2. Find the top slice for each half |
459
|
|
|
* // Step 3. Draw the slices from top to bottom |
460
|
|
|
* // |
461
|
|
|
* // The thing that slightly complicates this scheme with all the |
462
|
|
|
* // angle comparisons below is that we can have an arbitrary start |
463
|
|
|
* // angle so we must take into account the different equivalence classes. |
464
|
|
|
* // For the same reason we must walk through the angle array in a |
465
|
|
|
* // modulo fashion. |
466
|
|
|
* // |
467
|
|
|
* // Limitations of algorithm: |
468
|
|
|
* // * A small exploded slice which crosses the 270 degree point |
469
|
|
|
* // will get slightly nagged close to the center due to the fact that |
470
|
|
|
* // we print the slices in Z-order and that the slice left part |
471
|
|
|
* // get printed first and might get slightly nagged by a larger |
472
|
|
|
* // slice on the right side just before the right part of the small |
473
|
|
|
* // slice. Not a major problem though. |
474
|
|
|
*/ |
475
|
|
|
// Determine the height of the ellippse which gives an |
476
|
|
|
// indication of the inclination angle |
477
|
2 |
|
$h = ($angle / 90.0) * $d; |
478
|
2 |
|
$sum = 0; |
479
|
2 |
|
for ($i = 0; $i < safe_count($data); ++$i) { |
480
|
2 |
|
$sum += $data[$i]; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
// Special optimization |
484
|
2 |
|
if ($sum == 0) { |
|
|
|
|
485
|
|
|
return; |
486
|
|
|
} |
487
|
|
|
|
488
|
2 |
|
if ($this->labeltype == 2) { |
489
|
|
|
$this->adjusted_data = $this->AdjPercentage($data); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
// Setup the start |
493
|
2 |
|
$accsum = 0; |
|
|
|
|
494
|
2 |
|
$a = $startangle; |
495
|
2 |
|
$a = $this->NormAngle($a); |
496
|
|
|
|
497
|
|
|
// |
498
|
|
|
// Step 1 . Split all slices that crosses 90 or 270 |
499
|
|
|
// |
500
|
2 |
|
$idx = 0; |
501
|
2 |
|
$adjexplode = []; |
502
|
2 |
|
$numcolors = safe_count($colors); |
503
|
2 |
|
for ($i = 0; $i < safe_count($data); ++$i, ++$idx) { |
504
|
2 |
|
$da = $data[$i] / $sum * 360; |
505
|
|
|
|
506
|
2 |
|
if (empty($this->explode_radius[$i])) { |
507
|
2 |
|
$this->explode_radius[$i] = 0; |
508
|
|
|
} |
509
|
|
|
|
510
|
2 |
|
$expscale = 1; |
511
|
2 |
|
if ($aaoption == 1) { |
512
|
2 |
|
$expscale = 2; |
513
|
|
|
} |
514
|
|
|
|
515
|
2 |
|
$la = $a + $da / 2; |
516
|
2 |
|
$explode = [$xc + $this->explode_radius[$i] * cos($la * M_PI / 180) * $expscale, |
517
|
2 |
|
$yc - $this->explode_radius[$i] * sin($la * M_PI / 180) * ($h / $d) * $expscale, ]; |
518
|
2 |
|
$adjexplode[$idx] = $explode; |
519
|
2 |
|
$labeldata[$i] = [$la, $explode[0], $explode[1]]; |
520
|
2 |
|
$originalangles[$i] = [$a, $a + $da]; |
521
|
|
|
|
522
|
2 |
|
$ne = $this->NormAngle($a + $da); |
523
|
2 |
|
if ($da <= 180) { |
524
|
|
|
// If the slice size is <= 90 it can at maximum cut across |
525
|
|
|
// one boundary (either 90 or 270) where it needs to be split |
526
|
2 |
|
$split = -1; // no split |
527
|
2 |
|
if (($da <= 90 && ($a <= 90 && $ne > 90)) || |
528
|
2 |
|
(($da <= 180 && $da > 90) && (($a < 90 || $a >= 270) && $ne > 90))) { |
529
|
2 |
|
$split = 90; |
530
|
2 |
|
} elseif (($da <= 90 && ($a <= 270 && $ne > 270)) || |
531
|
2 |
|
(($da <= 180 && $da > 90) && ($a >= 90 && $a < 270 && ($a + $da) > 270))) { |
532
|
2 |
|
$split = 270; |
533
|
|
|
} |
534
|
2 |
|
if ($split > 0) { |
535
|
|
|
// split in two |
536
|
2 |
|
$angles[$idx] = [$a, $split]; |
537
|
2 |
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
538
|
2 |
|
$adjexplode[$idx] = $explode; |
539
|
2 |
|
$angles[++$idx] = [$split, $ne]; |
540
|
2 |
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
541
|
2 |
|
$adjexplode[$idx] = $explode; |
542
|
|
|
} else { |
543
|
|
|
// no split |
544
|
2 |
|
$angles[$idx] = [$a, $ne]; |
545
|
2 |
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
546
|
2 |
|
$adjexplode[$idx] = $explode; |
547
|
|
|
} |
548
|
|
|
} else { |
549
|
|
|
// da>180 |
550
|
|
|
// Slice may, depending on position, cross one or two |
551
|
|
|
// bonudaries |
552
|
|
|
|
553
|
|
|
if ($a < 90) { |
554
|
|
|
$split = 90; |
555
|
|
|
} elseif ($a <= 270) { |
556
|
|
|
$split = 270; |
557
|
|
|
} else { |
558
|
|
|
$split = 90; |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
$angles[$idx] = [$a, $split]; |
562
|
|
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
563
|
|
|
$adjexplode[$idx] = $explode; |
564
|
|
|
//if( $a+$da > 360-$split ) { |
565
|
|
|
// For slices larger than 270 degrees we might cross |
566
|
|
|
// another boundary as well. This means that we must |
567
|
|
|
// split the slice further. The comparison gets a little |
568
|
|
|
// bit complicated since we must take into accound that |
569
|
|
|
// a pie might have a startangle >0 and hence a slice might |
570
|
|
|
// wrap around the 0 angle. |
571
|
|
|
// Three cases: |
572
|
|
|
// a) Slice starts before 90 and hence gets a split=90, but |
573
|
|
|
// we must also check if we need to split at 270 |
574
|
|
|
// b) Slice starts after 90 but before 270 and slices |
575
|
|
|
// crosses 90 (after a wrap around of 0) |
576
|
|
|
// c) If start is > 270 (hence the firstr split is at 90) |
577
|
|
|
// and the slice is so large that it goes all the way |
578
|
|
|
// around 270. |
579
|
|
|
if (($a < 90 && ($a + $da > 270)) || ($a > 90 && $a <= 270 && ($a + $da > 360 + 90)) || ($a > 270 && $this->NormAngle($a + $da) > 270)) { |
580
|
|
|
$angles[++$idx] = [$split, 360 - $split]; |
581
|
|
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
582
|
|
|
$adjexplode[$idx] = $explode; |
583
|
|
|
$angles[++$idx] = [360 - $split, $ne]; |
584
|
|
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
585
|
|
|
$adjexplode[$idx] = $explode; |
586
|
|
|
} else { |
587
|
|
|
// Just a simple split to the previous decided |
588
|
|
|
// angle. |
589
|
|
|
$angles[++$idx] = [$split, $ne]; |
590
|
|
|
$adjcolors[$idx] = $colors[$i % $numcolors]; |
591
|
|
|
$adjexplode[$idx] = $explode; |
592
|
|
|
} |
593
|
|
|
} |
594
|
2 |
|
$a += $da; |
595
|
2 |
|
$a = $this->NormAngle($a); |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
// Total number of slices |
599
|
2 |
|
$n = safe_count($angles); |
|
|
|
|
600
|
|
|
|
601
|
2 |
|
for ($i = 0; $i < $n; ++$i) { |
602
|
2 |
|
list($dbgs, $dbge) = $angles[$i]; |
|
|
|
|
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
// |
606
|
|
|
// Step 2. Find start index (first pie that starts in upper left quadrant) |
607
|
|
|
// |
608
|
2 |
|
$minval = $angles[0][0]; |
609
|
2 |
|
$min = 0; |
610
|
2 |
|
for ($i = 0; $i < $n; ++$i) { |
611
|
2 |
|
if ($angles[$i][0] < $minval) { |
612
|
|
|
$minval = $angles[$i][0]; |
613
|
|
|
$min = $i; |
614
|
|
|
} |
615
|
|
|
} |
616
|
2 |
|
$j = $min; |
617
|
2 |
|
$cnt = 0; |
618
|
2 |
|
while ($angles[$j][1] <= 90) { |
619
|
2 |
|
++$j; |
620
|
2 |
|
if ($j >= $n) { |
621
|
|
|
$j = 0; |
622
|
|
|
} |
623
|
2 |
|
if ($cnt > $n) { |
624
|
|
|
Util\JpGraphError::RaiseL(14005); |
625
|
|
|
//("Pie3D Internal error (#1). Trying to wrap twice when looking for start index"); |
626
|
|
|
} |
627
|
2 |
|
++$cnt; |
628
|
|
|
} |
629
|
2 |
|
$start = $j; |
630
|
|
|
|
631
|
|
|
// |
632
|
|
|
// Step 3. Print slices in z-order |
633
|
|
|
// |
634
|
2 |
|
$cnt = 0; |
635
|
|
|
|
636
|
|
|
// First stroke all the slices between 90 and 270 (left half circle) |
637
|
|
|
// counterclockwise |
638
|
|
|
|
639
|
2 |
|
while ($angles[$j][0] < 270 && $aaoption !== 2) { |
640
|
2 |
|
list($x, $y) = $adjexplode[$j]; |
641
|
|
|
|
642
|
2 |
|
$this->Pie3DSlice( |
643
|
2 |
|
$img, |
644
|
2 |
|
$x, |
645
|
2 |
|
$y, |
646
|
2 |
|
$d, |
647
|
2 |
|
$h, |
648
|
2 |
|
$angles[$j][0], |
649
|
2 |
|
$angles[$j][1], |
650
|
2 |
|
$z, |
651
|
2 |
|
$adjcolors[$j], |
|
|
|
|
652
|
2 |
|
$shadow |
653
|
|
|
); |
654
|
|
|
|
655
|
2 |
|
$last = [$x, $y, $j]; |
656
|
|
|
|
657
|
2 |
|
++$j; |
658
|
2 |
|
if ($j >= $n) { |
659
|
|
|
$j = 0; |
660
|
|
|
} |
661
|
|
|
|
662
|
2 |
|
if ($cnt > $n) { |
663
|
|
|
Util\JpGraphError::RaiseL(14006); |
664
|
|
|
//("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking."); |
665
|
|
|
} |
666
|
2 |
|
++$cnt; |
667
|
|
|
} |
668
|
|
|
|
669
|
2 |
|
$slice_left = $n - $cnt; |
670
|
2 |
|
$j = $start - 1; |
671
|
2 |
|
if ($j < 0) { |
672
|
|
|
$j = $n - 1; |
673
|
|
|
} |
674
|
|
|
|
675
|
2 |
|
$cnt = 0; |
676
|
|
|
|
677
|
|
|
// The stroke all slices from 90 to -90 (right half circle) |
678
|
|
|
// clockwise |
679
|
2 |
|
while ($cnt < $slice_left && $aaoption !== 2) { |
680
|
2 |
|
list($x, $y) = $adjexplode[$j]; |
681
|
|
|
|
682
|
2 |
|
$this->Pie3DSlice( |
683
|
2 |
|
$img, |
684
|
2 |
|
$x, |
685
|
2 |
|
$y, |
686
|
2 |
|
$d, |
687
|
2 |
|
$h, |
688
|
2 |
|
$angles[$j][0], |
689
|
2 |
|
$angles[$j][1], |
690
|
2 |
|
$z, |
691
|
2 |
|
$adjcolors[$j], |
692
|
2 |
|
$shadow |
693
|
|
|
); |
694
|
2 |
|
--$j; |
695
|
2 |
|
if ($cnt > $n) { |
696
|
|
|
Util\JpGraphError::RaiseL(14006); |
697
|
|
|
//("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking."); |
698
|
|
|
} |
699
|
2 |
|
if ($j < 0) { |
700
|
2 |
|
$j = $n - 1; |
701
|
|
|
} |
702
|
|
|
|
703
|
2 |
|
++$cnt; |
704
|
|
|
} |
705
|
|
|
|
706
|
|
|
// Now do a special thing. Stroke the last slice on the left |
707
|
|
|
// halfcircle one more time. This is needed in the case where |
708
|
|
|
// the slice close to 270 have been exploded. In that case the |
709
|
|
|
// part of the slice close to the center of the pie might be |
710
|
|
|
// slightly nagged. |
711
|
2 |
|
if ($aaoption !== 2) { |
712
|
2 |
|
$this->Pie3DSlice( |
713
|
2 |
|
$img, |
714
|
2 |
|
$last[0], |
|
|
|
|
715
|
2 |
|
$last[1], |
716
|
2 |
|
$d, |
717
|
2 |
|
$h, |
718
|
2 |
|
$angles[$last[2]][0], |
719
|
2 |
|
$angles[$last[2]][1], |
720
|
2 |
|
$z, |
721
|
2 |
|
$adjcolors[$last[2]], |
722
|
2 |
|
$shadow |
723
|
|
|
); |
724
|
|
|
} |
725
|
|
|
|
726
|
2 |
|
if ($aaoption !== 1) { |
727
|
|
|
// Now print possible labels and add csim |
728
|
2 |
|
$this->value->ApplyFont($img); |
729
|
2 |
|
$margin = $img->GetFontHeight() / 2 + $this->value->margin; |
730
|
2 |
|
for ($i = 0; $i < safe_count($data); ++$i) { |
731
|
2 |
|
$la = $labeldata[$i][0]; |
|
|
|
|
732
|
2 |
|
$x = $labeldata[$i][1] + cos($la * M_PI / 180) * ($d + $margin) * $this->ilabelposadj; |
733
|
2 |
|
$y = $labeldata[$i][2] - sin($la * M_PI / 180) * ($h + $margin) * $this->ilabelposadj; |
734
|
2 |
|
if ($this->ilabelposadj >= 1.0) { |
735
|
2 |
|
if ($la > 180 && $la < 360) { |
736
|
2 |
|
$y += $z; |
737
|
|
|
} |
738
|
|
|
} |
739
|
2 |
|
if ($this->labeltype == 0) { |
740
|
2 |
|
if ($sum > 0) { |
741
|
2 |
|
$l = 100 * $data[$i] / $sum; |
742
|
|
|
} else { |
743
|
2 |
|
$l = 0; |
744
|
|
|
} |
745
|
|
|
} elseif ($this->labeltype == 1) { |
746
|
|
|
$l = $data[$i]; |
747
|
|
|
} else { |
748
|
|
|
$l = $this->adjusted_data[$i]; |
749
|
|
|
} |
750
|
2 |
|
if (isset($this->labels[$i]) && is_string($this->labels[$i])) { |
751
|
|
|
$l = sprintf($this->labels[$i], $l); |
752
|
|
|
} |
753
|
|
|
|
754
|
2 |
|
$this->StrokeLabels($l, $img, $labeldata[$i][0] * M_PI / 180, $x, $y, $z); |
755
|
|
|
|
756
|
2 |
|
$this->Add3DSliceToCSIM( |
757
|
2 |
|
$i, |
758
|
2 |
|
$labeldata[$i][1], |
759
|
2 |
|
$labeldata[$i][2], |
760
|
2 |
|
$h * 2, |
761
|
2 |
|
$d * 2, |
762
|
2 |
|
$z, |
763
|
2 |
|
$originalangles[$i][0], |
|
|
|
|
764
|
2 |
|
$originalangles[$i][1] |
765
|
|
|
); |
766
|
|
|
} |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
// |
770
|
|
|
// Finally add potential lines in pie |
771
|
|
|
// |
772
|
|
|
|
773
|
2 |
|
if ($edgecolor == '' || $aaoption !== 0) { |
774
|
2 |
|
return; |
775
|
|
|
} |
776
|
|
|
|
777
|
|
|
$accsum = 0; |
778
|
|
|
$a = $startangle; |
779
|
|
|
$a = $this->NormAngle($a); |
780
|
|
|
|
781
|
|
|
$a *= M_PI / 180.0; |
782
|
|
|
|
783
|
|
|
$idx = 0; |
784
|
|
|
$img->PushColor($edgecolor); |
785
|
|
|
$img->SetLineWeight($edgeweight); |
786
|
|
|
|
787
|
|
|
$fulledge = true; |
788
|
|
|
for ($i = 0; $i < safe_count($data) && $fulledge; ++$i) { |
789
|
|
|
if (empty($this->explode_radius[$i])) { |
790
|
|
|
$this->explode_radius[$i] = 0; |
791
|
|
|
} |
792
|
|
|
if ($this->explode_radius[$i] > 0) { |
793
|
|
|
$fulledge = false; |
794
|
|
|
} |
795
|
|
|
} |
796
|
|
|
|
797
|
|
|
for ($i = 0; $i < safe_count($data); ++$i, ++$idx) { |
798
|
|
|
$da = $data[$i] / $sum * 2 * M_PI; |
799
|
|
|
$this->StrokeFullSliceFrame( |
800
|
|
|
$img, |
801
|
|
|
$xc, |
802
|
|
|
$yc, |
803
|
|
|
$a, |
804
|
|
|
$a + $da, |
805
|
|
|
$d, |
806
|
|
|
$h, |
807
|
|
|
$z, |
808
|
|
|
$edgecolor, |
809
|
|
|
$this->explode_radius[$i], |
810
|
|
|
$fulledge |
811
|
|
|
); |
812
|
|
|
$a += $da; |
813
|
|
|
} |
814
|
|
|
$img->PopColor(); |
815
|
|
|
} |
816
|
|
|
|
817
|
|
|
public function StrokeFullSliceFrame($img, $xc, $yc, $sa, $ea, $w, $h, $z, $edgecolor, $exploderadius, $fulledge) |
818
|
|
|
{ |
819
|
|
|
$step = 0.02; |
820
|
|
|
|
821
|
|
|
if ($exploderadius > 0) { |
822
|
|
|
$la = ($sa + $ea) / 2; |
823
|
|
|
$xc += $exploderadius * cos($la); |
824
|
|
|
$yc -= $exploderadius * sin($la) * ($h / $w); |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
$p = [$xc, $yc, $xc + $w * cos($sa), $yc - $h * sin($sa)]; |
828
|
|
|
|
829
|
|
|
for ($a = $sa; $a < $ea; $a += $step) { |
830
|
|
|
$p[] = $xc + $w * cos($a); |
831
|
|
|
$p[] = $yc - $h * sin($a); |
832
|
|
|
} |
833
|
|
|
|
834
|
|
|
$p[] = $xc + $w * cos($ea); |
835
|
|
|
$p[] = $yc - $h * sin($ea); |
836
|
|
|
$p[] = $xc; |
837
|
|
|
$p[] = $yc; |
838
|
|
|
|
839
|
|
|
$img->SetColor($edgecolor); |
840
|
|
|
$img->Polygon($p); |
841
|
|
|
|
842
|
|
|
// Unfortunately we can't really draw the full edge around the whole of |
843
|
|
|
// of the slice if any of the slices are exploded. The reason is that |
844
|
|
|
// this algorithm is to simply. There are cases where the edges will |
845
|
|
|
// "overwrite" other slices when they have been exploded. |
846
|
|
|
// Doing the full, proper 3D hidden lines stiff is actually quite |
847
|
|
|
// tricky. So for exploded pies we only draw the top edge. Not perfect |
848
|
|
|
// but the "real" solution is much more complicated. |
849
|
|
|
if ($fulledge && !($sa > 0 && $sa < M_PI && $ea < M_PI)) { |
850
|
|
|
if ($sa < M_PI && $ea > M_PI) { |
851
|
|
|
$sa = M_PI; |
852
|
|
|
} |
853
|
|
|
|
854
|
|
|
if ($sa < 2 * M_PI && (($ea >= 2 * M_PI) || ($ea > 0 && $ea < $sa))) { |
855
|
|
|
$ea = 2 * M_PI; |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
if ($sa >= M_PI && $ea <= 2 * M_PI) { |
859
|
|
|
$p = [$xc + $w * cos($sa), $yc - $h * sin($sa), |
860
|
|
|
$xc + $w * cos($sa), $z + $yc - $h * sin($sa), ]; |
861
|
|
|
|
862
|
|
|
for ($a = $sa + $step; $a < $ea; $a += $step) { |
863
|
|
|
$p[] = $xc + $w * cos($a); |
864
|
|
|
$p[] = $z + $yc - $h * sin($a); |
865
|
|
|
} |
866
|
|
|
$p[] = $xc + $w * cos($ea); |
867
|
|
|
$p[] = $z + $yc - $h * sin($ea); |
868
|
|
|
$p[] = $xc + $w * cos($ea); |
869
|
|
|
$p[] = $yc - $h * sin($ea); |
870
|
|
|
$img->SetColor($edgecolor); |
871
|
|
|
$img->Polygon($p); |
872
|
|
|
} |
873
|
|
|
} |
874
|
|
|
} |
875
|
|
|
|
876
|
2 |
|
public function Stroke($img, $aaoption = 0) |
877
|
|
|
{ |
878
|
2 |
|
$n = safe_count($this->data); |
879
|
|
|
|
880
|
|
|
// If user hasn't set the colors use the theme array |
881
|
2 |
|
if ($this->setslicecolors == null) { |
882
|
2 |
|
$colors = array_keys($img->rgb->rgb_table); |
883
|
2 |
|
sort($colors); |
884
|
2 |
|
$idx_a = $this->themearr[$this->theme]; |
885
|
2 |
|
$ca = []; |
886
|
2 |
|
$m = safe_count($idx_a); |
887
|
2 |
|
for ($i = 0; $i < $m; ++$i) { |
888
|
2 |
|
$ca[$i] = $colors[$idx_a[$i]]; |
889
|
|
|
} |
890
|
2 |
|
$ca = array_reverse(array_slice($ca, 0, $n)); |
891
|
|
|
} else { |
892
|
|
|
$ca = $this->setslicecolors; |
893
|
|
|
} |
894
|
|
|
|
895
|
2 |
|
if ($this->posx <= 1 && $this->posx > 0) { |
896
|
2 |
|
$xc = round($this->posx * $img->width); |
897
|
|
|
} else { |
898
|
|
|
$xc = $this->posx; |
899
|
|
|
} |
900
|
|
|
|
901
|
2 |
|
if ($this->posy <= 1 && $this->posy > 0) { |
902
|
2 |
|
$yc = round($this->posy * $img->height); |
903
|
|
|
} else { |
904
|
|
|
$yc = $this->posy; |
905
|
|
|
} |
906
|
|
|
|
907
|
2 |
|
if ($this->radius <= 1) { |
908
|
2 |
|
$width = floor($this->radius * min($img->width, $img->height)); |
909
|
|
|
// Make sure that the pie doesn't overflow the image border |
910
|
|
|
// The 0.9 factor is simply an extra margin to leave some space |
911
|
|
|
// between the pie an the border of the image. |
912
|
2 |
|
$width = min($width, min($xc * 0.9, ($yc * 90 / $this->angle - $width / 4) * 0.9)); |
913
|
|
|
} else { |
914
|
1 |
|
$width = $this->radius * ($aaoption === 1 ? 2 : 1); |
915
|
|
|
} |
916
|
|
|
|
917
|
|
|
// Add a sanity check for width |
918
|
2 |
|
if ($width < 1) { |
919
|
|
|
Util\JpGraphError::RaiseL(14007); //("Width for 3D Pie is 0. Specify a size > 0"); |
920
|
|
|
} |
921
|
|
|
|
922
|
|
|
// Establish a thickness. By default the thickness is a fifth of the |
923
|
|
|
// pie slice width (=pie radius) but since the perspective depends |
924
|
|
|
// on the inclination angle we use some heuristics to make the edge |
925
|
|
|
// slightly thicker the less the angle. |
926
|
|
|
|
927
|
|
|
// Has user specified an absolute thickness? In that case use |
928
|
|
|
// that instead |
929
|
|
|
|
930
|
2 |
|
if ($this->iThickness) { |
931
|
1 |
|
$thick = $this->iThickness; |
932
|
1 |
|
$thick *= ($aaoption === 1 ? 2 : 1); |
933
|
|
|
} else { |
934
|
2 |
|
$thick = $width / 12; |
935
|
|
|
} |
936
|
2 |
|
$a = $this->angle; |
937
|
|
|
|
938
|
2 |
|
if ($a <= 30) { |
939
|
2 |
|
$thick *= 1.6; |
940
|
2 |
|
} elseif ($a <= 40) { |
941
|
|
|
$thick *= 1.4; |
942
|
2 |
|
} elseif ($a <= 50) { |
943
|
2 |
|
$thick *= 1.2; |
944
|
|
|
} elseif ($a <= 60) { |
945
|
|
|
$thick *= 1.0; |
946
|
|
|
} elseif ($a <= 70) { |
947
|
|
|
$thick *= 0.8; |
948
|
|
|
} elseif ($a <= 80) { |
949
|
|
|
$thick *= 0.7; |
950
|
|
|
} else { |
951
|
|
|
$thick *= 0.6; |
952
|
|
|
} |
953
|
|
|
|
954
|
2 |
|
$thick = floor($thick); |
|
|
|
|
955
|
|
|
|
956
|
2 |
|
if ($this->explode_all) { |
957
|
|
|
for ($i = 0; $i < $n; ++$i) { |
958
|
|
|
$this->explode_radius[$i] = $this->explode_r; |
959
|
|
|
} |
960
|
|
|
} |
961
|
|
|
|
962
|
2 |
|
$this->Pie3D( |
963
|
2 |
|
$aaoption, |
964
|
2 |
|
$img, |
965
|
2 |
|
$this->data, |
966
|
2 |
|
$ca, |
967
|
2 |
|
$xc, |
968
|
2 |
|
$yc, |
969
|
2 |
|
$width, |
970
|
2 |
|
$this->angle, |
971
|
2 |
|
$thick, |
972
|
2 |
|
0.65, |
973
|
2 |
|
$this->startangle, |
974
|
2 |
|
$this->edgecolor, |
975
|
2 |
|
$this->edgeweight |
976
|
|
|
); |
977
|
|
|
|
978
|
|
|
// Adjust title position |
979
|
2 |
|
if ($aaoption != 1) { |
980
|
2 |
|
$this->title->SetPos($xc, $yc - $this->title->GetFontHeight($img) - $width / 2 - $this->title->margin, 'center', 'bottom'); |
981
|
2 |
|
$this->title->Stroke($img); |
982
|
|
|
} |
983
|
2 |
|
} |
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* PRIVATE METHODS. |
987
|
|
|
* |
988
|
|
|
* @param mixed $label |
989
|
|
|
* @param mixed $img |
990
|
|
|
* @param mixed $a |
991
|
|
|
* @param mixed $xp |
992
|
|
|
* @param mixed $yp |
993
|
|
|
* @param mixed $z |
994
|
|
|
*/ |
995
|
|
|
|
996
|
|
|
// Position the labels of each slice |
997
|
2 |
|
public function StrokeLabels($label, $img, $a, $xp, $yp, $z) |
|
|
|
|
998
|
|
|
{ |
999
|
2 |
|
$this->value->halign = 'left'; |
1000
|
2 |
|
$this->value->valign = 'top'; |
1001
|
|
|
|
1002
|
|
|
// Position the axis title. |
1003
|
|
|
// dx, dy is the offset from the top left corner of the bounding box that sorrounds the text |
1004
|
|
|
// that intersects with the extension of the corresponding axis. The code looks a little |
1005
|
|
|
// bit messy but this is really the only way of having a reasonable position of the |
1006
|
|
|
// axis titles. |
1007
|
2 |
|
$this->value->ApplyFont($img); |
1008
|
2 |
|
$h = $img->GetTextHeight($label); |
1009
|
|
|
// For numeric values the format of the display value |
1010
|
|
|
// must be taken into account |
1011
|
2 |
|
if (is_numeric($label)) { |
1012
|
2 |
|
if ($label >= 0) { |
1013
|
2 |
|
$w = $img->GetTextWidth(sprintf($this->value->format, $label)); |
1014
|
|
|
} else { |
1015
|
2 |
|
$w = $img->GetTextWidth(sprintf($this->value->negformat, $label)); |
1016
|
|
|
} |
1017
|
|
|
} else { |
1018
|
|
|
$w = $img->GetTextWidth($label); |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
2 |
|
while ($a > 2 * M_PI) { |
1022
|
|
|
$a -= 2 * M_PI; |
1023
|
|
|
} |
1024
|
|
|
|
1025
|
2 |
|
if ($a >= 7 * M_PI / 4 || $a <= M_PI / 4) { |
1026
|
2 |
|
$dx = 0; |
1027
|
|
|
} |
1028
|
|
|
|
1029
|
2 |
|
if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) { |
1030
|
2 |
|
$dx = ($a - M_PI / 4) * 2 / M_PI; |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
2 |
|
if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) { |
1034
|
2 |
|
$dx = 1; |
1035
|
|
|
} |
1036
|
|
|
|
1037
|
2 |
|
if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) { |
1038
|
2 |
|
$dx = (1 - ($a - M_PI * 5 / 4) * 2 / M_PI); |
1039
|
|
|
} |
1040
|
|
|
|
1041
|
2 |
|
if ($a >= 7 * M_PI / 4) { |
1042
|
2 |
|
$dy = (($a - M_PI) - 3 * M_PI / 4) * 2 / M_PI; |
1043
|
|
|
} |
1044
|
|
|
|
1045
|
2 |
|
if ($a <= M_PI / 4) { |
1046
|
1 |
|
$dy = (1 - $a * 2 / M_PI); |
1047
|
|
|
} |
1048
|
|
|
|
1049
|
2 |
|
if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) { |
1050
|
2 |
|
$dy = 1; |
1051
|
|
|
} |
1052
|
|
|
|
1053
|
2 |
|
if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) { |
1054
|
2 |
|
$dy = (1 - ($a - 3 * M_PI / 4) * 2 / M_PI); |
1055
|
|
|
} |
1056
|
|
|
|
1057
|
2 |
|
if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) { |
1058
|
2 |
|
$dy = 0; |
1059
|
|
|
} |
1060
|
|
|
|
1061
|
2 |
|
$x = round($xp - $dx * $w); |
|
|
|
|
1062
|
2 |
|
$y = round($yp - $dy * $h); |
|
|
|
|
1063
|
|
|
|
1064
|
|
|
// Mark anchor point for debugging |
1065
|
|
|
/* |
1066
|
|
|
$img->SetColor('red'); |
1067
|
|
|
$img->Line($xp-10,$yp,$xp+10,$yp); |
1068
|
|
|
$img->Line($xp,$yp-10,$xp,$yp+10); |
1069
|
|
|
*/ |
1070
|
|
|
|
1071
|
2 |
|
$oldmargin = $this->value->margin; |
1072
|
2 |
|
$this->value->margin = 0; |
1073
|
2 |
|
$this->value->Stroke($img, $label, $x, $y); |
1074
|
2 |
|
$this->value->margin = $oldmargin; |
1075
|
2 |
|
} |
1076
|
|
|
} // @class |
1077
|
|
|
|
1078
|
|
|
/* EOF */ |
1079
|
|
|
|