1: <?php
2: namespace SamChristy\PieChart;
3: include 'PieChart.php';
4:
5: 6: 7: 8: 9: 10: 11: 12:
13: class PieChartImagick extends PieChart {
14: public function destroy() {
15: $this->canvas->destroy();
16: }
17:
18: public function draw() {
19: $this->canvas = new Imagick;
20: $this->canvas->newImage($this->width, $this->height, $this->backgroundColor->toHex());
21:
22: $total = 0;
23: $sliceStart = -90;
24:
25: $titleHeight = $this->_drawTitle();
26: $legendWidth = $this->_drawLegend($titleHeight);
27:
28:
29: $pieCenterX = ($this->width - $legendWidth) / 2;
30:
31:
32: $pieCenterY = $titleHeight + ($this->height - $titleHeight) / 2;
33:
34:
35: $pieDiameter = round(
36: min($this->width - $legendWidth, $this->height - $titleHeight) * 0.85
37: );
38:
39: $pieRadius = $pieDiameter / 2;
40:
41: foreach ($this->slices as $slice)
42: $total += $slice['value'];
43:
44:
45: foreach ($this->slices as &$slice) {
46: $sliceWidth = 360 * $slice['value'] / $total;
47:
48:
49: if ($sliceWidth == 0) continue;
50:
51: $sliceEnd = $sliceStart + $sliceWidth;
52:
53: $this->_drawSlice(
54: array('x' => $pieCenterX, 'y' => $pieCenterY),
55: $pieRadius,
56: $sliceStart,
57: $sliceEnd,
58: $slice['color']
59: );
60:
61:
62: $sliceStart = $sliceEnd;
63: }
64: }
65:
66: protected function _output($method, $format, $filename) {
67: switch ($format) {
68: case parent::FORMAT_GIF:
69: $this->canvas->setImageFormat('gif');
70:
71: if ($method == parent::OUTPUT_INLINE || $method == parent::OUTPUT_DOWNLOAD) {
72: return print $this->canvas;
73: }
74: else if ($method == parent::OUTPUT_SAVE) {
75: return $this->canvas->writeImage($filename);
76: }
77: break;
78:
79: case parent::FORMAT_JPEG:
80: $this->canvas->setImageFormat('jpeg');
81: $this->canvas->setImageCompression(imagick::COMPRESSION_JPEG);
82: $this->canvas->setImageCompressionQuality($this->quality);
83:
84: if ($method == parent::OUTPUT_INLINE || $method == parent::OUTPUT_DOWNLOAD) {
85: return print $this->canvas;
86: }
87: else if ($method == parent::OUTPUT_SAVE) {
88: return $this->canvas->writeImage($filename);
89: }
90: break;
91:
92: case parent::FORMAT_PNG:
93: $this->canvas->setImageFormat('png');
94:
95: if ($method == parent::OUTPUT_INLINE || $method == parent::OUTPUT_DOWNLOAD) {
96: return print $this->canvas;
97: }
98: else if ($method == parent::OUTPUT_SAVE) {
99: return $this->canvas->writeImage($filename);
100: }
101: break;
102: }
103:
104: return false;
105: }
106:
107: 108: 109: 110: 111:
112: protected function _drawLegend($legendOffset) {
113: if (!$this->hasLegend)
114: return 0;
115:
116:
117: $legendFontSize = $this->width * 0.0325;
118:
119:
120: if (ceil($legendFontSize) < 8)
121: return 0;
122:
123:
124: $squareSize = $this->height * 0.060;
125: $squarePadding = $this->height * 0.025;
126: $labelPadding = $this->height * 0.025;
127:
128: $sliceCount = count($this->slices);
129:
130: $legendPadding = 0.075 * $this->width;
131:
132:
133: $maxLabelWidth = $this->_maxLabelWidth($legendFontSize);
134:
135:
136: $legendWidth = $squareSize + $labelPadding + $maxLabelWidth;
137: $legendHeight = $sliceCount * ($squareSize + $squarePadding) - $squarePadding;
138:
139:
140: if ($legendWidth + $legendPadding * 2 > $this->width / 2)
141: return 0;
142:
143: if ($legendHeight > $this->height - $legendOffset - $legendPadding * 2)
144: return 0;
145:
146: $legendX = $this->width - $legendWidth - $legendPadding;
147: $legendY = ($this->height - $legendOffset) / 2 + $legendOffset - $legendHeight / 2;
148:
149: $labelSettings = new ImagickDraw;
150:
151: $labelSettings->setFont($this->legendFont);
152: $labelSettings->setFillColor($this->textColor->toHex());
153: $labelSettings->setFontSize($legendFontSize);
154: $labelSettings->setGravity(Imagick::GRAVITY_NORTHWEST);
155:
156: $i = 0;
157: foreach ($this->slices as $sliceName => $slice) {
158:
159: $OffsetY = $i++ * ($squareSize + $squarePadding);
160:
161: $keyPosX = $legendX;
162: $keyPosY = $legendY + $OffsetY;
163:
164:
165: $keySquare = new ImagickDraw;
166: $keySquare->setFillColor($slice['color']->toHex());
167: $keySquare->rectangle(
168: $keyPosX,
169: $keyPosY,
170: $keyPosX + $squareSize,
171: $keyPosY + $squareSize
172: );
173:
174: $this->canvas->drawImage($keySquare);
175:
176:
177: $labelMetrics = $this->canvas->queryFontMetrics($labelSettings, $sliceName);
178:
179: $this->canvas->annotateImage(
180: $labelSettings,
181: $keyPosX + $squareSize + $squarePadding,
182: $keyPosY + $labelMetrics['descender'] / 2,
183: 0,
184: $sliceName
185: );
186: }
187:
188: return $legendWidth + $legendPadding;
189: }
190:
191: protected function _drawSlice($center, $radius, $start, $end, $color){
192:
193: $radius -= 1;
194: $center['x'] -= 1;
195: $center['y'] -= 1;
196:
197:
198: $arc = new ImagickDraw;
199: $color = $color->toHex();
200:
201: $arc->setFillColor($color);
202: $arc->setStrokeColor($color);
203: $arc->setStrokeWidth(1);
204:
205: $arc->arc(
206: $center['x'] - $radius,
207: $center['y'] - $radius,
208: $center['x'] + $radius,
209: $center['y'] + $radius,
210: $start,
211: $end
212: );
213:
214: $this->canvas->drawImage($arc);
215:
216:
217: $startRadians = deg2rad($start);
218: $endRadians = deg2rad($end);
219:
220:
221: $startPosition = array(
222: 'x' => $center['x'] + $radius * cos($startRadians),
223: 'y' => $center['y'] + $radius * sin($startRadians)
224: );
225:
226:
227: $endPosition = array(
228: 'x' => $center['x'] + $radius * cos($endRadians),
229: 'y' => $center['y'] + $radius * sin($endRadians)
230: );
231:
232: $trianglePoints = array($startPosition, $endPosition, $center);
233:
234: $triangle = new ImagickDraw;
235:
236: $triangle->setFillColor($color);
237: $triangle->setStrokeColor($color);
238: $triangle->setStrokeWidth(1);
239:
240: $triangle->polygon($trianglePoints);
241:
242: $this->canvas->drawImage($triangle);
243: }
244:
245: 246: 247: 248:
249: protected function _maxLabelWidth($fontSize) {
250: $labelSettings = new ImagickDraw;
251: $labelSettings->setFontSize($fontSize);
252: $widestLabelWidth = 0;
253:
254: foreach ($this->slices as $sliceName => $slice) {
255:
256: $labelMetrics = $this->canvas->queryFontMetrics($labelSettings, $sliceName);
257: $labelWidth = $labelMetrics['textWidth'];
258:
259: if ($labelWidth > $widestLabelWidth)
260: $widestLabelWidth = $labelWidth;
261: }
262:
263: return $widestLabelWidth;
264: }
265:
266: 267: 268: 269: 270:
271: protected function _drawTitle() {
272: if (!$this->title) return 0;
273:
274: $titleSettings = new ImagickDraw;
275:
276: $titleSettings->setFont($this->titleFont);
277: $titleSettings->setFillColor($this->textColor->toHex());
278: $titleSettings->setGravity(Imagick::GRAVITY_NORTH);
279:
280:
281: $titleSize = 0.08 * $this->height;
282: $minTitleSize = 10;
283:
284: do {
285: $titleSettings->setFontSize($titleSize);
286:
287:
288: $titleBBox = $this->canvas->queryFontMetrics($titleSettings, $this->title);
289: $titleWidth = $titleBBox['textWidth'];
290:
291:
292: if ($titleWidth <= ($this->width * 0.9))
293: break;
294:
295: $titleSize -= 0.5;
296: } while ($titleSize >= $minTitleSize);
297:
298: $titleHeight = $titleBBox['textHeight'];
299:
300:
301: if ($titleSize < $minTitleSize) return 0;
302:
303:
304: $titleTopPadding = 0.075 * $this->height;
305:
306:
307: $this->canvas->annotateImage($titleSettings, 0, $titleTopPadding, 0, $this->title);
308:
309: return $titleHeight + $titleTopPadding;
310: }
311: }