pie_width = $this->width * (self::PIE_WIDTH_PERCENT / 100); $this->pie_height = $this->width * (self::PIE_HEIGHT_PERCENT / 100); $this->pie_center_y = $this->height * (self::PIE_CENTER_Y_OFFSET / 100); $this->pie_center_x = $this->width * (self::PIE_CENTER_X_OFFSET / 100); //set data label spacing if ($this->bool_data_labels) { //set to number of pixels that are equal to text width //7 is a base spacer that all labels get $this->pie_data_label_space = 7 + $this->width / 30; $this->pie_width *= self::PIE_LABEL_SCALE / 100; $this->pie_height *= self::PIE_LABEL_SCALE / 100; } if ($this->bool_legend) { //compensate for legend with lesser preset percent $this->pie_width *= self::PIE_LEGEND_SCALE / 100; $this->pie_height *= self::PIE_LEGEND_SCALE / 100; $this->pie_center_x *= self::PIE_CENTER_LEGEND_SCALE / 100; } $this->pie_3D_height = self::PIE_3D_HEIGHT_PERCENT * ($this->pie_width / 100); } protected function setupData() { //in the pie extension, this will calculate the total sum and the corresponding percentages if ($this->data_set_count == 1) { $sum = array_sum($this->data_array[0]); if ($sum > 0) { foreach ($this->data_array[0] as $dataText => $dataValue) { $this->pie_data_array_percents[] = $dataValue / $sum; //find data text length $len = strlen($dataText); if ($len > $this->pie_data_max_length) { $this->pie_data_max_length = $len; } } $this->bool_bars_generate = true; } else { $this->bool_bars_generate = false; $this->error[] = "Sum of data must be greater than 0."; } } else { $this->error[]="Multiple datasets not allowed with pie charts"; } } protected function generateLegend() { $maxChars = NULL; //calc height / width based on # of values $pie_legend_height = (self::PIE_LEGEND_TEXT_HEIGHT * $this->data_count) + (2 * self::PIE_LEGEND_PADDING); $pie_legend_width = ($this->pie_data_max_length * self::PIE_LEGEND_TEXT_WIDTH) + (6 * self::PIE_LEGEND_PADDING); //allotted space does not include padding around legend (smaller) $allottedSpace = $this->width - $this->pie_center_x - ($this->pie_width / 2) - (2 * self::PIE_LEGEND_PADDING); if ($this->bool_data_labels) { //also compensate for displayed text data % on graph $allottedSpace -= ((4 + $this->pie_precision) * self::PIE_LABEL_TEXT_WIDTH) + $this->pie_data_label_space; } //check to make sure we are not > allotted space if ($pie_legend_width > $allottedSpace) { //if we are, adjust width and max length for data values //4 = padding | swatch(padding width) | padding | ...text... |padding $swatchAndPaddingWidth = 4 * self::PIE_LEGEND_PADDING; //MAX CHARS = ALOTTED SPACE - ENOUGH ROOM FOR SWATCHES / TEXT WIDTH $maxChars = floor(($allottedSpace - $swatchAndPaddingWidth) / self::PIE_LEGEND_TEXT_WIDTH); $pie_legend_width = ($maxChars * self::PIE_LEGEND_TEXT_WIDTH) + $swatchAndPaddingWidth; } else { //we didnt go over allotted space, so we should adjust the center of the pie chart now $equalSpacing = ($this->width - ($this->pie_width + $pie_legend_width)) / 3; //so now reposition center at spacing + 1/2 pie width $this->pie_center_x = ($this->pie_width / 2) + $equalSpacing; } //auto adjusting formula for position of pie_legend_x based on pie chart size $a = ($this->pie_center_x + $this->pie_width / 2); $b = $this->width - $a; $c = ($b - $pie_legend_width) / 2; //set pie x & y args $this->pie_legend_x = $a + $c; $this->pie_legend_y = ($this->height - $pie_legend_height) / 2; //background imagefilledrectangle($this->image, $this->pie_legend_x, $this->pie_legend_y, $this->pie_legend_x + $pie_legend_width, $this->pie_legend_y + $pie_legend_height, $this->legend_color); //border imagerectangle($this->image, $this->pie_legend_x, $this->pie_legend_y, $this->pie_legend_x + $pie_legend_width, $this->pie_legend_y + $pie_legend_height, $this->legend_outline_color); $xValue = $this->pie_legend_x + self::PIE_LEGEND_PADDING; $count = 0; $this->resetColorPointer(); $swatchToTextOffset = (self::PIE_LEGEND_TEXT_HEIGHT - 6) / 2; $swatchSize = self::PIE_LEGEND_TEXT_HEIGHT - (2 * $swatchToTextOffset); foreach ($this->data_array[0] as $dataText => $dataValue) { $yValue = $this->pie_legend_y + self::PIE_LEGEND_TEXT_HEIGHT * $count + self::PIE_LEGEND_PADDING; //draw color boxes $color = $this->generateNextColor(); imagefilledrectangle($this->image, $xValue, $yValue + $swatchToTextOffset, $xValue + $swatchSize, $yValue + $swatchToTextOffset + $swatchSize, $color); imagerectangle($this->image, $xValue, $yValue + $swatchToTextOffset, $xValue + $swatchSize, $yValue + $swatchToTextOffset + $swatchSize, $this->legend_swatch_outline_color); //if longer than our max, trim text if ($maxChars) { $dataText = substr($dataText,0, $maxChars); } imagestring($this->image, 2, $xValue + (2 * self::PIE_LEGEND_PADDING), $yValue, $dataText, $this->legend_text_color); $count++; } } protected function generateBars() { $this->resetColorPointer(); //loop through and create shadaing for ($i = $this->pie_center_y + $this->pie_3D_height; $i > $this->pie_center_y; $i--) { $arcStart = 0; foreach ($this->pie_data_array_percents as $key => $value) { $color = $this->generateNextColor(true); // generate a darker version of the indexed color // do not draw if the value is zero if (! $value == 0){ imagefilledarc($this->image, $this->pie_center_x, $i, $this->pie_width, $this->pie_height, $arcStart, (360 * $value) + $arcStart, $color, IMG_ARC_PIE); $arcStart += 360*$value; } } $this->resetColorPointer(); } $arcStart = 0; foreach ($this->pie_data_array_percents as $key => $value) { $color = $this->generateNextColor(); // do not draw if the value is zero if (! $value == 0){ imagefilledarc($this->image, $this->pie_center_x, $this->pie_center_y, $this->pie_width, $this->pie_height, $arcStart, (360*$value)+$arcStart, $color, IMG_ARC_PIE); $arcStart += 360 * $value; } if ($this->bool_data_labels) { $this->generateDataLabel($value, $arcStart); } } } protected function generateDataLabel($value, $arcStart) { //midway if the mid arc angle of the wedge we just drew $midway = ($arcStart - (180 * $value)); //adjust for ellipse height/width ratio $skew = self::PIE_HEIGHT_PERCENT / self::PIE_WIDTH_PERCENT; $pi = atan(1.0) * 4.0; $theta = ($midway / 180) * $pi; $valueX = $this->pie_center_x + ($this->pie_width / 2 + $this->pie_data_label_space) * cos($theta); $valueY = $this->pie_center_y + ($this->pie_width / 2 + $this->pie_data_label_space) * sin($theta) * $skew; $displayValue = $this->formatPercent($value); $valueArray = $this->dataLabelHandicap($valueX, $valueY, $displayValue, $midway); $valueX = $valueArray[0]; $valueY = $valueArray[1]; imagestring($this->image, 2, $valueX, $valueY, $displayValue, $this->label_text_color); } protected function formatPercent($input) { return number_format($input * 100, $this->pie_precision) . '%'; } protected function dataLabelHandicap($x, $y, $value, $midway) { //moves data label x/y based on quadrant and length of displayed data //and how text is displayed (upper left corner x/y) //extra 1 for % sign $lengthOffset = (strlen($value) * (self::PIE_LABEL_TEXT_WIDTH)) / 2; $vertOffset = self::PIE_LABEL_TEXT_HEIGHT / 2; if ($midway <= 30) { $newX = $x - (1.5 * $lengthOffset); $newY = $y - $vertOffset; } elseif ($midway > 30 && $midway <= 135) { $newX = $x - $lengthOffset; $newY = $y - $vertOffset + $this->pie_3D_height; } elseif ($midway > 135 && $midway <= 165) { $newX = $x - $lengthOffset; $newY = $y - $vertOffset; } elseif ($midway > 165 && $midway <= 200) { //value at risk for being out of bounds on smaller graphs $newX = $x - (1/3 * $lengthOffset); $newY = $y - $vertOffset; } elseif ($midway > 200 && $midway <= 330) { $newX = $x - $lengthOffset; $newY = $y - $vertOffset; } elseif ($midway > 330) { //value at risk for overlapping the legend on smaller graphs $newX = $x - (1.5 * $lengthOffset); $newY = $y - $vertOffset; } else { $newX = $x - $lengthOffset; $newY = $y - $vertOffset; } return array($newX, $newY); } protected function generateNextColor($dark = false) { $array = $this->returnColorArray($this->pie_avail_colors[$this->pie_color_pointer]); if ($dark) { //we are trying to generate a darker version of the existing color $array[0] *= .8; $array[1] *= .8; $array[2] *= .8; } $color = imagecolorallocate($this->image, $array[0], $array[1], $array[2]); $this->pie_color_pointer++; if ($this->pie_color_pointer >= count($this->pie_avail_colors)) { $this->pie_color_pointer = 0; } return $color; } protected function resetColorPointer() { $this->pie_color_pointer = 0; } protected function returnColorArray($color) { //this function first checks exisitng colors in phpgraphlib //then if not found checks its own list //comes with various preset lighter pie chart friendly colors if ($resultColor = parent::returnColorArray($color)) { return $resultColor; } else { //remove last error generated (phpgraphlib::returncolorarray) sets only one error if false) array_pop($this->error); //check to see if numeric color passed through in form '128,128,128' if (strpos($color,',') !== false) { return explode(',', $color); } switch(strtolower($color)) { //named colors based on w3c's recommended html colors case 'pastel_orange_1': return array(238,197,145); break; case 'pastel_orange_2': return array(238,180,34); break; case 'pastel_blue_1': return array(122,197,205); break; case 'pastel_green_1': return array(102,205,0); break; case 'pastel_blue_2': return array(125,167,217); break; case 'pastel_green_2': return array(196,223,155); break; case 'clay': return array(246,142,85); break; case 'pastel_yellow': return array(255,247,153); break; case 'pastel_purple': return array(135,129,189); break; case 'brown': return array(166,124,81); break; } $this->error[] = "Color name \"$color\" not recogized."; return false; } } protected function generateTitle() { //draws title b/t top of graph and edge of canvas $pieTop = $this->pie_center_y - ($this->pie_height / 2); if ($this->bool_legend) { $topElement = ($pieTop < $this->pie_legend_y) ? $pieTop : $this->pie_legend_y; } else { $topElement = $pieTop; } if ($topElement < 0) { $this->error[] = "Not enough room for a title. Increase graph height, or eliminate data values."; } else { $title_y = ($topElement / 2) - (self::TITLE_CHAR_HEIGHT / 2); $title_x = ($this->width / 2) - ((strlen($this->title_text) * self::TITLE_CHAR_WIDTH) / 2); imagestring($this->image, 2, $title_x , $title_y , $this->title_text, $this->title_color); } } public function setLabelTextColor($color) { $this->setGenericColor($color, '$this->label_text_color', "Label text color not specified properly."); } public function setPrecision($digits) { if (is_int($digits)) { $this->pie_precision = $digits; return; } $this->error[] = "Integer arg for setPrecision() not specified properly."; } public function setDataLabels($bool) { if (is_bool($bool)) { $this->bool_data_labels = $bool; return; } $this->error[] = "Boolean arg for setDataLabels() not specified properly."; } //overwritten and unused PHPGraphLib functions function setDataPoints($bool) { $this->error[] = __function__ . '() function not allowed in PHPGraphLib Stacked extension.'; } function calcTopMargin() {} function calcRightMargin() {} function setupGrid() {} }