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