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