Amélioration : camembert

Contenu du snippet

Bonjour à tous

Je viens de développer un camembert 2D dans le but de m'entrainer, mais aussi de ressembler au mieux à celui généré par Flex 4 (j'ai un projet sous Flex 4 et j'ai un module d'impression PDF qui doit rendre le même résultat que Flex 4, d'où la nécessité de coder le mien, les autres ayant à chaque fois au moins un détail gênant qui diffère).

J'aimerais vos avis et suggestions sur ce qui a été fait. :)

Il s'agit d'une version 1, et plusieurs améliorations sont à venir:
- explodeSlice(): pour détacher un morceau du reste du camembert (mise en retrait)
- drawVLegend(): contrairement à drawHLegend() qui dessine la légende horizontalement à la base, celui-ci les dessinera à gauche ou à droite et verticalement
- set3D(): pour une version 3D du camembert
- et d'autres encore

Le point où j'aimerais aussi avoir part de vos idées, c'est celui pour rendre le graphique plus joli (éviter de voir les pixels).
J'ai essayé d'appliquer l'antialiasing, mais soit je ne m'en sers pas correctement, soit il est inefficace ici...

Ci-dessous:
- AKPie.php : la classe du graphique
- test.php : un exemple d'utilisation
- et il faudra inclure "font.ttf" qui n'est autre que la police courrier par défaut dans C:\Windows\Font\ (pour windows)

Voilà :)

Et merci à ceux qui auront le courage de lire, voire de tester ;)

Source / Exemple :


// --- test.php -----------------------------------------------
include 'AKPie.php';

// --- Call example 
// test.php?seriesX=a:11:{s:10:%22Logistique%22;s:2:%2216%22;s:18:%22Cabinet%20de%20conseil%22;s:1:%227%22;s:14:%22Pharmaceutique%22;s:1:%227%22;s:10:%22Automobile%22;s:1:%227%22;s:28:%22SSII%20et%20Conseil%20Informatique%22;s:1:%227%22;s:8:%22T%E9l%E9coms%22;s:1:%227%22;s:55:%22Administration%20d%27Etat,%20Poste,%20Collectivit%E9%20Territoriale%22;s:1:%224%22;s:8:%22B%E2timent%22;s:1:%223%22;s:6:%22M%E9dias%22;s:1:%223%22;s:9:%22Industrie%22;s:1:%223%22;s:5:%22Autre%22;s:1:%227%22;}
// test.php?seriesX=a:3:{i:0;i:2009;i:1;i:2010;i:2;i:2011;}&seriesY=a:3:{i:0;i:118;i:1;i:218;i:2;i:780;}

// --- Data retrieving
$seriesX = (!empty($_GET['seriesX'])) 
	? unserialize(stripslashes($_GET['seriesX']))
	: array();
$seriesY = (!empty($_GET['seriesY']))
	? unserialize(stripslashes($_GET['seriesY']))
	: array(); 

// --- Class instanciation
$pie = new AKPie($seriesX, $seriesY);
$pie->draw();

// --- AKPie.php ---------------------------------------------------------------------------------------
<?php
/* ------------------------------------------------------------ *

  • ___ ___ *
  • / // / *
  • / // / __ __ *
  • / // \ / /__ __ __ __/ /_ _ _ _ __ ____ *
  • / /| |\ \ __ __ / //_/ _//_ |/_ __/_/ \/ _ |/ _/ *
  • / /_| | \ \ . \ . \ // / /_/ . | / / / / / / / | /\_ \ *
  • /________| \__\ _/ _///_/\__/\__|_\\_//_/\__/_/ /_/____/ *
  • / / / / *
  • /_/ /_/ *
  • *
  • ------------------------------------------------------------ *
  • Class: AKPie *
  • *
  • @author: Asph@tor (under Advanced Partner's project) *
  • @date: 2011-05-17 *
  • @version: v1.0.0 *
  • @compatible: PHP >= 4.2 *
  • @dependancies: GD2 *
  • font.ttf *
  • @license: free of use, of modifications and of *
  • commercialization. Just past this header *
  • at the begining of your code *
  • @contact: akpplications@gmail.com *
  • ------------------------------------------------------------ */
class AKPie { protected $img; // The chart instance (true colors) protected $antialias; // Antialiasing protected $backgroundColor; // Background color behind the chart protected $borderColor; // Chart border's color protected $pieBorderColor; // Pie border's color protected $pieDelta; // Distance between pie and legend's labels protected $piePadding; // Chart's padding protected $pieX; // Chart's origin x value protected $pieY; // Chart's origin y value protected $pieSize; // Chart's size protected $width; // Display area width protected $height; // Display area height protected $title; // Chart's title protected $titleColor; // Chart title's color protected $titleFont; // Chart title's font to use protected $titlePadding; // Chart title's distance from the nearest side protected $titleSize; // Chart title's size protected $legendLabelColor; // Legend item label's color protected $legendLabelGap; // Gap between legend's labels protected $legendLabelSize; // Legend item label's size protected $legendPadding; // Legend box's distance from the nearest side protected $legendPosition; // Legend position (left, bottom, right, top) protected $legendWidth; // Legend width protected $guidLabelSize; // Legend guid label's size protected $guidLinesColor; // Legend guid line's color protected $guidLabelsColor; // Legend guid label's color protected $seriesX; // Data keys' label protected $seriesY; // Data values' label protected $seriesZ; // Data values in percents protected $precision; // Percents precision protected $showBorder; // Show chart's borders protected $showLegend; // Show legend protected $showLegendBorder; // Show legend's border protected $showPieBorder; // Show pie's and slices' border protected $showShadow; // Show pie's shadow protected $showGuidLines; // Show lines between slices and slices' label protected $showGuidLabels; // Show slices' labels protected $titleY; // First free y after title's drawing protected $legendY; // Last free y after legend's drawing protected $slicesColors; // Chart's color range protected $shadowColor; // Pie shadow's color protected $shadowLagX; // Pie shadow's x distance from pie protected $shadowLagY; // Pie shadow's y distance from pie // --------------------------------------------------------- // --- CONSTRUCTOR --- // --------------------------------------------------------- function __construct($seriesX=array(), $seriesY=array()) { if (count($seriesY) == 0) { $this->seriesX = array_keys($seriesX); $this->seriesY = array_values($seriesX); } else if (count($seriesX) == 0 || count($seriesY) != count($seriesX)) { echo "ERROR: incorrect parameters - size of each parameters should be the same!"; } else { $this->seriesX = array_values($seriesX); $this->seriesY = array_values($seriesY); } $this->width = 800; $this->height = 600; $this->precision = 2; $this->antialias = true; $this->title = 'My Pie Chart'; $this->titleColor = '333333'; $this->titleFont = 'font.ttf'; $this->titlePadding = 40; $this->titleSize = 16; $this->titleY = 0; $this->backgroundColor = '#FFFFFF'; $this->borderColor = '0x444444'; $this->legendY = $this->height; $this->legendLabelColor = 'ff0'; $this->legendLabelSize = 8; $this->legendLabelGap = $this->legendLabelSize*3/4; $this->legendPadding = 20; $this->legendPosition = 'bottom'; $this->legendWidth = 250; $this->guidLabelSize = 8; $this->guidLinesColor = '0x444444'; $this->guidLabelsColor = '0x444444'; $this->pieBorderColor = 'ccc'; $this->pieDelta = 20; $this->piePadding = 0; $this->showBorder = true; $this->showLegend = true; $this->showLegendBorder = true; $this->showPieBorder = true; $this->showShadow = true; $this->showGuidLines = true; $this->showGuidLabels = true; $this->shadowColor = '444'; $this->shadowLagX = 3; $this->shadowLagY = 3; $this->slicesColors = array( '0xE48701', '0x1B85D9', '0xA5BC4E', '0xFFFC93', '0x7ECFE1', '0xFF84BC', '0xC5F57E', '0x7EA6CE', '0xFF7E9F', '0x7EC29B', '0x7E83BB', '0xFFB17E', '0x7EC9BD', '0xC57EBF', '0xFFBD8A'); } // --------------------------------------------------------- // --- SETTERS --- // --------------------------------------------------------- function setAntialias($antialias) { $this->antialias = $antialias; } function setBackgroundColor($color) { $this->backgroundColor = $color; } function setBorderColor($color) { $this->borderColor = $color; } function setPieBorderColor($color) { $this->pieBorderColor = $color; } function setPieDelta($delta) { $this->pieDelta = $delta; } function setPiePadding($padding) { $this->piePadding = $padding; } function setWidth($width) { $this->width = $width; } function setHeight($height) { $this->height = $height; } function setTitle($title) { $this->title = $title; } function setTitleFont($font) { $this->titleFont = $font; } function setTitleColor($color) { $this->titleColor = $color; } function setTitleSize($size) { $this->titleSize = $size; } function setTitlePadding($padding) { $this->titlePadding = $padding; } function setLabelFunction($function) { $this->labelFunction = $function; } function setLegendWidth($width) { $this->legendWidth = $width; } function setLegendLabelColor($color) { $this->legendLabelColor = $color; } function setLegendLabelGap($gap) { $this->legendLabelGap = $gap; } function setLegendLabelSize($size) { $this->legendLabelSize = $size; $this->legendLabelGap = $size*3/4; } function setLegendPadding($padding) { $this->legendPadding = $padding; } function setLegendPosition($position) { $this->legendPosition = $position; } function setGuidLabelSize($size) { $this->guidLabelSize = $size; } function setGuidLabelsColor($color) { $this->guidLabelsColor = $color; } function setGuidLinesColor($color) { $this->guidLinesColor = $color; } function setPrecision($precision) { $this->precision = $precision; } function showBorder() { $this->showBorder = true; } function showLegend() { $this->showLegend = true; } function showLegendBorder() { $this->showLegendBorder = true; } function showPieBorder() { $this->showPieBorder = true; } function showShadow() { $this->showShadow = true; } function showGuidLines() { $this->showGuidLines = true; } function showGuidLabels() { $this->showGuidLabels = true; } function setSlicesColors($colors) { $this->slicesColors = $colors; } function setShadowColor($color) { $this->shadowColor = $color; } function setShadowLagX($lag) { $this->shadowLagX = $lag; } function setShadowLagY($lag) { $this->shadowLagY = $lag; } // --------------------------------------------------------- // --- PUBLIC METHODS --- // --------------------------------------------------------- function draw() { $this->img = imagecreatetruecolor($this->width, $this->height); imageantialias($this->img, $this->antialias); $this->drawBackground(); $this->drawBorder(); $this->drawTitle(); $this->drawHLegend(); $this->drawPie(); $this->drawGuidLines(); header('Content-Type: image/png'); imagepng($this->img); imagedestroy($this->img); } // --------------------------------------------------------- // --- PRIVATE METHODS --- // --------------------------------------------------------- protected function calculatePercents($data) { $percents = array(); $len = count($data); $total = 0; foreach ($data as $v) $total+=$v; foreach ($data as $v) $percents[] = round($v * 100 / $total, $this->precision); return $percents; } protected function formatGuidLabel($label, $width) { $str = explode(' ', $label); $len = count($str); $line = 1; $txt = $str[0]; $label = ''; $maxWidth = 0; for ($i=1; $i<$len; $i++) { if (strlen($txt.' '.$str[$i])*$this->guidLabelSize*3/4 < $width) $txt .= ' '.$str[$i]; else { $w = strlen($txt)*$this->guidLabelSize*3/4; if ($maxWidth < $w) $maxWidth = $w; $label .= $txt."\n"; $txt = $str[$i]; $line++; } } $label .= $txt; return array($label, $line, $maxWidth); } protected function drawBackground() { list($r, $g, $b) = $this->html2rgb($this->backgroundColor); $bgcolor = imagecolorallocate($this->img, $r, $g, $b); imagefilledrectangle($this->img, 0, 0, $this->width-1, $this->height-1, $bgcolor); } protected function drawBorder() { if ($this->showBorder) { list($r, $g, $b) = $this->html2rgb($this->borderColor); $bgcolor = imagecolorallocate($this->img, $r, $g, $b); imagerectangle($this->img, 0, 0, $this->width-1, $this->height-1, $bgcolor); } } protected function drawTitle() { // POSSIBLE OPTIMIZATION: word wrapping $x = ($this->width - (strlen($this->title) * $this->titleSize*5/6)) / 2; $y = $this->titlePadding; list($r, $g, $b) = $this->html2rgb($this->titleColor); $titleColor = imagecolorallocate($this->img, $r, $g, $b); imagettftext($this->img, $this->titleSize, 0, $x, $y, $titleColor, $this->titleFont, $this->title); $this->titleY = $y + $this->titlePadding; } protected function drawHLegend() { if ($this->showLegend) { // box sizes and paddings $cellSize = $this->legendLabelSize * 0.75; $textSize = $this->legendLabelSize; $cellPadding = ($textSize - $cellSize) / 2; // lengths and item/line numbers $len = count($this->seriesX); $max = 0; $nb = 0; $flag = $this->width - 2*$this->legendPadding; // colors $slices = count($this->slicesColors); list($r, $g, $b) = $this->html2rgb($this->borderColor); $borderColor = imagecolorallocate($this->img, $r, $g, $b); // longest label width for ($i=0; $i<$len; $i++) if (strlen($this->seriesX[$i]) > strlen($this->seriesX[$max])) $max = $i; $wordWidth = strlen($this->seriesX[$max]) * $textSize; // number of lines and items per line $cellWidth = 3*$cellSize + $wordWidth; $cellHeight = $textSize + $cellSize; while ($flag > 0 && $nb < $len) { $flag -= $cellWidth; $nb++; } if ($cellWidth * $nb > $this->width - $this->legendPadding*2) $nb--; $line = ceil($len / $nb); // positions switch ($this->legendPosition) { case "top": $x = ($this->width - $nb*$cellWidth) / 2; $y = $this->titleY; $this->legendY = $this->height; break; case "bottom": default: $x = ($this->width - $nb*$cellWidth) / 2 ; $y = $this->height - ($textSize+$this->legendLabelGap)*$line - $this->legendPadding; $this->legendY = $y - $this->legendPadding; break; } // draw items for ($i=0; $i<$line; $i++) { for ($j=0; $j<$nb && $j<$len-$i*$nb; $j++) { // draw color cell list($r, $g, $b) = $this->html2rgb($this->slicesColors[($i*$nb+$j)%$slices]); $legendColor = imagecolorallocate($this->img, $r, $g, $b); imagefilledrectangle( $this->img, $x+1+$j*$cellWidth, $y+$cellPadding+1+$i*$cellHeight, $x+$cellSize-2+$j*$cellWidth, $y+$cellPadding+$cellSize-2+$i*$cellHeight, $legendColor); imagerectangle( $this->img, $x+$j*$cellWidth, $y+$cellPadding+$i*$cellHeight, $x+$cellSize-1+$j*$cellWidth, $y+$cellPadding+$cellSize-1+$i*$cellHeight, $borderColor); // draw label imagettftext( $this->img, $textSize, 0, $x+2*$cellSize+$j*$cellWidth, $y+$textSize-1+$i*$cellHeight, $titleColor, $this->titleFont, $this->seriesX[$i*$nb+$j]); } } // draw border if ($this->showLegendBorder) imagerectangle( $this->img, $x-$this->legendPadding/2, $y-$this->legendPadding/2, $x+$nb*$cellWidth, $y+$line*$cellHeight, $borderColor); if ($this->legendPosition == 'top') $this->titleY = $y+$line*$cellHeight+$this->legendPadding; } } protected function drawPie() { // position $x = $this->width / 2; $y = ($this->titleY + $this->legendY) / 2; $pieSize = $this->height - ($this->titleY + $this->height - $this->legendY + 2*$this->piePadding); $pieSize *= 3/5; $this->pieX = $x; $this->pieY = $y; $this->pieSize = $pieSize; // shadow if ($this->showShadow) { list($r, $g, $b) = $this->html2rgb($this->shadowColor); $color = imagecolorallocate($this->img, $r, $g, $b); imagefilledarc($this->img, $x+$this->shadowLagX, $y+$this->shadowLagY, $pieSize, $pieSize, 0, 360, $color, IMG_ARC_PIE); } // calculate percents $this->seriesZ = $this->calculatePercents($this->seriesY); // slices drawing $len = count($this->seriesX); $slen = count($this->slicesColors); list($r, $g, $b) = $this->html2rgb($this->pieBorderColor); $borderColor = imagecolorallocate($this->img, $r, $g, $b); $deg = 0; for ($i=0; $i<$len; $i++) { list($r, $g, $b) = $this->html2rgb($this->slicesColors[$i%$slen]); $color = imagecolorallocate($this->img, $r, $g, $b); $end = ($i==$len-1) ? 360 : $deg + $this->seriesZ[$i] * 360 / 100; imagefilledarc($this->img, $x, $y, $pieSize, $pieSize, $deg, $end, $color, IMG_ARC_PIE); if ($this->showPieBorder) imagefilledarc($this->img, $x, $y, $pieSize, $pieSize, $deg, $end, $borderColor, IMG_ARC_EDGED | IMG_ARC_NOFILL); $deg = $end; } // IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL | IMG_ARC_CHORD } protected function drawGuidLines() { if ($this->showGuidLabels) { // data retrieving $x = $this->pieX; $y = $this->pieY; $d = $this->pieSize/2; $xl = $x - ($d + $this->pieDelta); $xr = $x + ($d + $this->pieDelta); list($r, $g, $b) = $this->html2rgb($this->guidLinesColor); $color = imagecolorallocate($this->img, $r, $g, $b); // drawing $deg = 0; $v = $this->seriesZ[0]; foreach ($this->seriesZ as $k => $v) { $tmp = ($v*2*pi()/100); // lines $xx = ($x + ($d + $this->pieDelta)*cos($deg + $tmp/2)) > $x ? $xr : $xl; if ($this->showGuidLines) { imageline( $this->img, $x + ($d - $this->pieDelta)*cos($deg + $tmp/2), $y + ($d - $this->pieDelta)*sin($deg + $tmp/2), $x + ($d + $this->pieDelta)*cos($deg + $tmp/2), $y + ($d + $this->pieDelta)*sin($deg + $tmp/2), $color); imageline( $this->img, $x + ($d + $this->pieDelta)*cos($deg + $tmp/2), $y + ($d + $this->pieDelta)*sin($deg + $tmp/2), $xx, $y + ($d + $this->pieDelta)*sin($deg + $tmp/2), $color); } // labels if ($xx == $xr) { list($txt, $nb, $w) = $this->formatGuidLabel($this->seriesX[$k], $this->width - $xr - $this->pieDelta); $xx = $xr + $this->pieDelta; } else { $width = strlen($this->seriesX[$k])*$this->guidLabelSize*3/4; list($txt, $nb, $w) = $this->formatGuidLabel($this->seriesX[$k], $xl - $this->pieDelta); $xx = $xl - $this->pieDelta - ($nb > 1 ? $w : $width); } imagettftext( $this->img, $this->guidLabelSize, 0, $xx, $y + ($d+$this->pieDelta)*sin($deg+$tmp/2) + $this->guidLabelSize/2 - ($nb-1)*$this->guidLabelSize/2, $this->guidLabelsColor, $this->titleFont, $txt); $deg += $tmp; } } } // --------------------------------------------------------- // --- STATIC METHODS --- // --------------------------------------------------------- static function html2rgb($color) { if ($color[0] == '#') $color = substr($color, 1); if ($color[0] == '0' && $color[1] == 'x') $color = substr($color, 2); if (strlen($color) == 6) return array( hexdec($color[0].$color[1]), hexdec($color[2].$color[3]), hexdec($color[4].$color[5])); elseif (strlen($color) == 3) return array( hexdec($color[0].$color[0]), hexdec($color[1].$color[1]), hexdec($color[2].$color[2])); else return false; } static function rgb2html($r, $g=-1, $b=-1) { if (is_array($r) && sizeof($r) == 3) list($r, $g, $b) = $r; $r = intval($r); $g = intval($g); $b = intval($b); $r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r)); $g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g)); $b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b)); $color = (strlen($r) < 2 ? '0' : '').$r; $color .= (strlen($g) < 2 ? '0' : '').$g; $color .= (strlen($b) < 2 ? '0' : '').$b; return '#'.$color; } } ?>

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.