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