Accueil > > > SMOOTH 3D CAMEMBERT
SMOOTH 3D CAMEMBERT
Information sur la source
Description
Suite à la remarque de BADOUX concernant le "camembert" de la source N°34131 d'adresse : http://www.phpcs.com/codes/CAMEMBERTS-3D_34131.a spx et dont les bords lui semblaient "un peux pixellisé", j'ai décidé de revoir tout le code avec la ferme intention de faire disparaître ces vilains petits défauts (d'où cette nouvelle publication et non pas une simple mise à jour). Un mot-clé à retenir : Anti-Aliasing (AA pour les connaisseurs) Sa définition : "algorithme appliqué sur le contour des formes pour en rendre l'aspect plus agréable, et éviter de voir la structure grillagée de l'image." La méthode "bourrin" consiste à créer une image plus grande (par exemple 3 fois plus grande)que l'on réduit à la bonne taille une fois le travail effectué. L'AA est réalisé par la fonction de réduction "ImageCopyResampled()", qui se charge de trouver un compromis satisfaisant entre les pixels constituant chaque carré de 3x3. Cette technique est efficace, l'image finale qu'elle produit acceptable (pour un camembert!), le code l'implémentant court et facile à comprendre, son seul défaut est de requérir un temps d'exécution important (que je n'ai pas mesuré, mais bon...). On peut trouver un code de ce type en allant sur "http://www.phpclasses.org" (y rechercher quelque chose comme "EQ_Smooth_3D_Pie_Graph"). On peut aussi attaquer le problème par la face nord, en partant d'une image à l'échelle 1, et gérer les transitions entre les zones de couleurs différentes. Cette technique suppose en fait la réécriture des fonctions de gd2 impliquées dans l'image, et l'utilisation intensive d'algorithmes qui ont pour nom : Bresenham, Gupta-Sproull, point-milieu, ainsi que la mise à contribution de la transparence alpha. C'est cette deuxième méthode qui est traitée ici. Le code ci-dessous est entièrement fonctionnel, on peut donc en faire un copier-coller et l'exécuter immédiatement (php5 et gd2 requis). Pour ceux qui veulent approfondir, ils trouveront dans le zip une version plus aboutie utilisant une véritable police de caractères et donnant le choix entre trois tailles de camembert différentes et trois types de fonds différents. La démo en ligne : http://michel.vanthodiep.free.fr/smooth3dcamembe rt/
Source
- <?php
- header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
- header ('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
- header ('Cache-Control: no-cache, must-revalidate');
- header ('Pragma: no-cache');
-
- /*
- - Date de création : 27/01/2008
- - Nom : aacamembert.php, version simplifiée à "copier-coller"
- - Auteur : opossum_farceur
- - Object : Camembert, 3D, Antialiasing, Bresenham, Statistiques
- */
- define('coef',M_PI/180);
-
- class aacamembert
- {
- private $img;
- private $a,$b,$cx,$cy,$aa,$bb,$aabb; /* ellipse */
- private $data,$sort,$ellipse; /* tableaux */
- private $quarter,$pivot; /* autres */
- private $num,$linecolor; /* algorithme de Gupta-Sproull */
-
- /* constructeur ---------------------------------------------------------------------------------------*/
-
- function __construct($title,$arr)
- {
- $width=500;
- $head=35; /* 15+15+5 : réservés pour le titre */
- $height=300;
- $w=240; /* grand axe horizontal */
- $h=$w/2; /* 120 : petit axe vertical */
- $thickness=$w/4; /* 60 : "épaisseur" du camembert */
- $topcolor=0xFFFF00; /* dégradé : couleur du haut */
- $bottomcolor=0x00FFFF; /* dégradé : couleur du bas */
- $fontsize=3;
- /* attributs calculés */
- $this->a=$w/2; /* 1/2 grand axe : 120 */
- $this->b=$h/2; /* 1/2 petit axe : 60 */
- $this->cx=$width/2; /* abscisse et ordonnée du "centre" du camembert */
- $this->cy=($height+$head-$thickness)/2;
- $this->aa=$this->a*$this->a;
- $this->bb=$this->b*$this->b;
- $this->aabb=$this->aa*$this->bb;
- /* attributs renseignés en cours de route */
- $this->data=array();
- $this->sort=array();
- $this->ellipse=array();
- /* antialiasing ligne droite */
- $this->num=10; /* à choisir pair */
- $this->linecolor=array(); /* taille du tableau : 1+3/2*$this->num, soit 16 (suffisant) */
-
- /* préliminaires */
- $this->img=imagecreatetruecolor($width,$height);
- $this->mybackground($width-1,$height-1,$topcolor,$bottomcolor);
- $textcolor=imagecolorallocate($this->img,0x00,0x00,0x00);
- imagestring($this->img,5,($width-imagefontwidth(5)*strlen($title))/2,15,$title,$textcolor);
- $this->aaellipse();
- $this->gupta_sproull();
- $specialcase=$this->init($arr,$thickness);
-
- /* tracés */
- $this->aadraw($thickness,$fontsize,$textcolor,$specialcase);
-
- /* affichage */
- header('Content-type: image/png');
- imagepng($this->img);
- imagedestroy($this->img);
- }
-
- /* "splite" une couleur en ses 3 composantes ----------------------------------------------------------*/
-
- private function hex2rgb($hex)
- {
- return array(($hex>>16)& 0xFF,($hex>>8)& 0xFF,$hex & 0xFF);
- }
-
- /* mise en place du fond ------------------------------------------------------------------------------*/
-
- private function mybackground($w,$h,$topcolor,$bottomcolor)
- {
- list($red,$green,$blue)=$this->hex2rgb($topcolor);
- $bc=$this->hex2rgb($bottomcolor);
- $incr=($bc[0]-$red)/$h;
- $incg=($bc[1]-$green)/$h;
- $incb=($bc[2]-$blue)/$h;
-
- for($j=0;$j<=$h;$j++,$red+=$incr,$green+=$incg,$blue+=$incb)
- imageline($this->img,0,$j,$w,$j,imagecolorallocate($this->img,$red,$green,$blue));
- }
-
- /* à partir de l'angle donné en argument retourne l'index correspondant du tableau de l'ellipse -------*/
-
- private function index($v)
- { /* formulation du sinus à partir du cosinus, en principe + rapide */
- $cosv=cos(coef*$v);
- $sinv= ($v>180)? -sqrt(1-$cosv*$cosv) : sqrt(1-$cosv*$cosv);
- $x=round($this->a*abs($cosv));
- $y=round($this->b*abs($sinv));
-
- $case=floor($v/90);
- $p=($case+$case%2)*$this->quarter;
- $sign= ($case%2)? -1 : 1;
- $q= ($this->aa*$y-$this->bb*$x<0)? $y : $this->quarter-$x;
-
- return $p+$sign*$q;
- }
-
- /* traitement des données fournies dans le tableau ----------------------------------------------------*/
-
- private function init($arr,$e)
- { /* calcul de la somme des données */
- for ($j=$sum=0,$n=count($arr);$j<$n;$j++) $sum+=$arr[$j][0];
- /* analyse du tableau */
- for ($j=$k=$v1=0;$j<$n;$j++) if ($arr[$j][0]) { /* cas non traité si donnée nulle */
- /* extraction des composantes red, green et blue */
- list($red,$green,$blue)=$this->hex2rgb($arr[$j][1]);
- $this->data[$k]['color']=imagecolorallocate($this->img,$red,$green,$blue);
- /* traitement des angles */
- $v2=$v1+$arr[$j][0]*360/$sum;
- if ($v1<=270 && $v2>=270) $first=$k;
- if ($v1<=90 && $v2>=90) $last=$k;
- $this->data[$k]['v']=$v2;
- /* détermine la couleur ombrée */
- $this->data[$k]['shade']=imagecolorallocate($this->img,max(0,$red-50),max(0,$green-50),max(0,$blue-50));
- /* préparation du texte */
- $this->data[$k]['legend']=$arr[$j][2];
- $this->data[$k]['value']=number_format(100*$arr[$j][0]/$sum,2,',','').'%';
- $this->data[$k]['xploded']=$arr[$j][3];
-
- $v1=$v2;
- $k++;
- }
-
- $m=count($this->data); /* différent de $n si une ou plusieurs donnée(s) sont nulles */
- if ($first!=$last) {
- for ($j=0,$k=$first;$k!=$last;$j++,$k=($k+1)%$m) $this->sort[$j]=$k;
- $this->pivot=$j;
- for ($k=$first-1;$k>=$last;$j++,$k--) $this->sort[$j]=$k;
-
- return false;
- }
- else { /* cas particulier préoccupant : une grosse portion présente à la fois à 12h et à 18h */
- for ($j=0,$k=($first+1)%$m;$j<$m;$j++,$k=($k+1)%$m) $this->sort[$j]=$k;
- $this->pivot=$m-1;
- /* mise en place du 2ème panneau de la dernière portion (origine du code : méthode "aadraw()") */
- $j=$this->sort[$m-1];
- $xploded=$this->data[$j]['xploded'];
- if ($this->data[$this->sort[0]]['xploded'] || $xploded) {
- $shade=$this->data[$j]['shade'];
- $v1= ($j)? $this->data[$j-1]['v'] : 0;
- $v2=$this->data[$j]['v'];
- $vm=($v1+$v2)/2;
- if ($xploded) {
- $ox=round($this->cx+$this->a*cos(coef*$vm)/6); /* a/6, b/6 : "offsets" des portions */
- $oy=round($this->cy+$this->b*sin(coef*$vm)/6);
- }
- else {
- $ox=$this->cx;
- $oy=$this->cy;
- }
- $index2=$this->index($v2);
- $x2=$ox+$this->ellipse[$index2]['x'];
- $y2=$oy+$this->ellipse[$index2]['y'];
-
- imageline($this->img,$ox,$oy,$ox,$oy+$e,$shade);
- imageline($this->img,$x2,$y2,$x2,$y2+$e,$shade);
- $this->aaborder($ox,$oy,$x2,$y2,0,$backup2,$shade); /* ligne du haut */
- $this->aaborder($x2,$y2+$e,$ox,$oy+$e,1,$backup2,$shade);/* ligne du bas (attention à l'ordre!) */
-
- if ($x2-$ox>1) imagefilltoborder($this->img,round(($ox+$x2)/2),round(($oy+$y2+$e)/2),$shade,$shade);
- $this->aaborder($x2,$y2+$e,$ox,$oy+$e,2,$backup2); /* aa ligne du bas */
- }
- return true;
- }
- }
-
- /* calcul des 16 tons de gris utiles au dessin des lignes droites antialiassées -----------------------*/
-
- private function gupta_sproull()
- { /* cette fonction crée une table allant de 0 à 1.5*$this->num */
- $col=array();
- $numdiv2=$this->num/2;
- /* calcul du volume interceptant chaque colonne, i et j parcourent un quart du cône */
- for ($i=$this->num,$total=0;$i>=0;$i--) {
- for ($j=$this->num,$k=0,$ii=$i*$i;$j>=0;$j--) {
- $d=sqrt($ii+$j*$j);
- if ($d<$this->num) $k+=$this->num-$d;
- }
- $col[$this->num-$i]=2*$k;
- $total+=4*$k;
- }
- /* calcul de la table : 1ere partie correspondant à une intersection d'épaisseur inférieure à 1 */
- $max=$this->num+$numdiv2;
- for ($i=$k=0;$i<=$this->num;$i++) {
- $k+=$col[$i];
- $this->linecolor[$max-$i]=imagecolorexactalpha($this->img,0,0,0,round(127*(1-$k/$total)));
- }
- /* calcul de la table : 2eme partie correspondant à une intersection d'épaisseur supérieure à 1 */
- for ($i=1;$i<=$numdiv2;$i++) {
- $k+=$col[$this->num-$i]-$col[$i];
- $this->linecolor[$numdiv2-$i]=imagecolorexactalpha($this->img,0,0,0,round(127*(1-$k/$total)));
- }
- }
-
- /* "allume" le pixel avec une intensité fonction de la distance à la droite ---------------------------*/
-
- private function plot($x,$y,$d)
- {
- if ($d<0) $d=-$d;
- if ($d>1.5) return; /* rien à allumer si le pixel est trop loin */
- imagesetpixel($this->img,$x,$y,$this->linecolor[round($d*$this->num)]);
- }
-
-
- /* tracé de droite "antialiassée" ---------------------------------------------------------------------*/
-
- private function aaline($x1,$y1,$x2,$y2)
- {
- $dx=abs($x2-$x1);
- $dy=abs($y2-$y1);
- $incj= (($x2-$x1)*($y2-$y1)>0)? 1 : -1;
- $K=1/(2*sqrt($dx*$dx+$dy*$dy));
-
- if ($dx>$dy) { /* tendance "horizontale" */
- if ($x2>$x1) {
- $y=$y1;
- $begin=$x1;
- $end=$x2;
- }
- else {
- $y=$y2;
- $begin=$x2;
- $end=$x1;
-
- }
-
- $p=2*$dy;
- $delta=$dx;
- $incx=0;
- $incy=$incj;
- $i=&$x;
- $j=&$y;
- }
- else { /* tendance "verticale" */
- if ($y2>$y1) {
- $x=$x1;
- $begin=$y1;
- $end=$y2;
- }
- else {
- $x=$x2;
- $begin=$y2;
- $end=$y1;
- }
-
- $p=2*$dx;
- $delta=$dy;
- $incx=$incj;
- $incy=0;
- $i=&$y;
- $j=&$x;
- }
- /* tronc commun */
- for ($i=$begin,$e=$p-$delta,$q=$e-$delta,$D=0,$R=2*$K*$delta;$i<=$end;$i++) {
- $this->plot($x,$y,$D);
- $this->plot($x+$incx,$y+$incy,$R-$D);
- $this->plot($x-$incx,$y-$incy,$R+$D);
- if ($e>=0) {
- $D=$K*($e-$delta);
- $j+=$incj;
- $e+=$q;
- }
- else {
- $D=$K*($e+$delta);
- $e+=$p;
- }
- }
- }
-
- /* traitement de l'aa des "bordures" ------------------------------------------------------------------*/
-
- private function aaborder($x1,$y1,$x2,$y2,$mode,&$backup,$mycolor=0)
- {
- $dx=$x2-$x1;
- $dy=$y2-$y1;
- $incj= ($dx*$dy>0)? 1 : -1;
- if ($mode==1) $backup=array();
- elseif ($mode==2) $const=$dx*$y1-$dy*$x1;
-
- if (abs($dx)>abs($dy)) { /* tendance "horizontale" */
- if ($dx>0) {
- $y=$y1;
- $begin=$x1;
- $end=$x2;
- }
- else {
- $y=$y2;
- $begin=$x2;
- $end=$x1;
- }
-
- $p=2*abs($dy);
- $e=$p-abs($dx);
- $q=$e-abs($dx);
- $i=&$x;
- $j=&$y;
- }
- else { /* tendance "verticale" */
- if ($dy>0) {
- $x=$x1;
- $begin=$y1;
- $end=$y2;
- }
- else {
- $x=$x2;
- $begin=$y2;
- $end=$y1;
- }
-
-
- $p=2*abs($dx);
- $e=$p-abs($dy);
- $q=$e-abs($dy);
- $i=&$y;
- $j=&$x;
- }
- /* tronc commun */
- for ($i=$begin,$t=0;$i<=$end;$i++,$t++) {
- if ($mode==1) $backup[]=imagecolorat($this->img,$x,$y); /* mode "sauvegarde" */
- elseif ($mode==2) { /* mode "antialiasing" */
- for ($f=$x-0.2,$g1=$y-0.2,$g3=$y+0.2,$alpha=0;$f<=$x+0.2;$f+=0.2) {
- $k=$const+$dy*$f;
- for ($g=$g1;$g<=$g3;$g+=0.2) if ($dx*$g>$k) $alpha++;
- }
- list($red,$green,$blue)=$this->hex2rgb($backup[$t]);
- $mycolor=imagecolorexactalpha($this->img,$red,$green,$blue,11*$alpha+28);
- }
-
- imagesetpixel($this->img,$x,$y,$mycolor);
-
- if ($e>=0) {
-
- $j+=$incj;
- $e+=$q;
- }
- else $e+=$p;
- }
- }
-
- /* tracé du camembert ---------------------------------------------------------------------------------*/
-
- private function aadraw($e,$fontsize,$textcolor,$specialcase)
- {
- $A=$this->a*21/16; /* 1/2 grand axe de l'ellipse "englobante" */
- $B=3*$A/4; /* bien que dépendant de $e, mais bon... */
- $dy=-$e/2; /* "centre" du camembert à celui de l'ellipse "englobante" */
- $AA=$A*$A;
- $BB=$B*$B;
- $ifw=imagefontwidth($fontsize);
-
- /* "grande boucle" */
- for ($i=0,$m=count($this->data);$i<$m;$i++) {
- $j=$this->sort[$i];
-
- /* initialisations */
- $color=$this->data[$j]['color'];
- $shade=$this->data[$j]['shade'];
- $v1= ($j)? $this->data[$j-1]['v'] : 0;
- $v2=$this->data[$j]['v'];
- $vm=($v1+$v2)/2;
- $index1=$this->index($v1);
- $index2=$this->index($v2);
- $xploded=$this->data[$j]['xploded'];
- $panel1=$panel2=false;
- $flag=0;
-
- if ($xploded) {
- $px=$this->a*cos(coef*$vm);
- $py=$this->b*sin(coef*$vm);
- $ox=round($this->cx+$px/6); /* a/6, b/6 : "offsets" des portions */
- $oy=round($this->cy+$py/6);
- }
- else {
- $px=5*$this->a*cos(coef*$vm)/6;
- $py=5*$this->b*sin(coef*$vm)/6;
- $ox=$this->cx;
- $oy=$this->cy;
-
- switch ($i) { /* cumulations possibles */
- case 0: $flag=$this->data[$this->sort[$this->pivot]]['xploded'];
- case $this->pivot-1: $flag|=$this->data[$this->sort[$m-1]]['xploded'];
- default: $flag|=$this->data[$this->sort[$i+1]]['xploded'];
- }
- }
-
- $x1=$ox+$this->ellipse[$index1]['x'];
- $y1=$oy+$this->ellipse[$index1]['y'];
- $x2=$ox+$this->ellipse[$index2]['x'];
- $y2=$oy+$this->ellipse[$index2]['y'];
- $gx=round($this->cx+$px); /* sans arrondi, problème raccordement segment en size 1 */
- $gy=round($this->cy+$py);
-
- /* traitement secteur du bas + paravent vertical */
- if ($v1<180) {
- if ($v2<180) {
- $indexmax=$index2;
- $x=$x2;
- $y=$y2;
- }
- else { /* le traitement s'arrête à 180° */
- $indexmax=2*$this->quarter; /* aussi : $this->index(180); */
- $x=$ox-$this->a; /* aussi : $ox+$this->ellipse[$indexmax]['x']; */
- $y=$oy; /* aussi : $oy+$this->ellipse[$indexmax]['y'] */
- }
- /* dessin des secteurs hauts et bas de l'ellipse */
- for ($k=$index1,$backup0=array();$k<=$indexmax;$k++) {
- $xk=$ox+$this->ellipse[$k]['x'];
- $yk=$oy+$this->ellipse[$k]['y'];
- $backup0[]=imagecolorat($this->img,$xk,$yk+$e);
- imagesetpixel($this->img,$xk,$yk,$shade);
- imagesetpixel($this->img,$xk,$yk+$e,$shade);
- }
- /* lignes verticales */
- imageline($this->img,$x1,$y1,$x1,$y1+$e,$shade);
- imageline($this->img,$x,$y,$x,$y+$e,$shade);
- /* "remplissage" peu optimisé mais valable dans tous les cas de figures */
- if ($x1-$x>1) imagefilltoborder($this->img,$x+1,$y+$e/2,$shade,$shade);
- /* antialiasing secteur ellipse du bas */
- $this->aaarc($index1,$indexmax,$ox,$oy+$e,$backup0);
- }
-
- if ($xploded || $flag) { /* dessine les "panneaux" verticaux */
- if ($v1>90 && $v1<270) {
- imageline($this->img,$ox,$oy,$ox,$oy+$e,$shade);
- imageline($this->img,$x1,$y1,$x1,$y1+$e,$shade); /* parfois inutile, mais bon... */
- $this->aaborder($x1,$y1,$ox,$oy,0,$backup1,$shade); /* ligne du haut, backup inutile */
- $this->aaborder($ox,$oy+$e,$x1,$y1+$e,1,$backup1,$shade);/* ligne du bas, backup parfois inutile */
-
- if ($ox-$x1>1)
- imagefilltoborder($this->img,round(($ox+$x1)/2),round(($oy+$y1+$e)/2),$shade,$shade);
- $this->aaborder($ox,$oy+$e,$x1,$y1+$e,2,$backup1); /* aa ligne du bas */
- if ($v1<180) imageline($this->img,$x1,$y1,$x1,$y1+$e,$color); /* séparation verticale */
- $panel1=true;
- }
- if ($v2<90 ||($v2>270 &&!($specialcase && $i==$m-1))) { /* $specialcase n'apparait qu'ici */
- imageline($this->img,$ox,$oy,$ox,$oy+$e,$shade);
- imageline($this->img,$x2,$y2,$x2,$y2+$e,$shade);
- $this->aaborder($ox,$oy,$x2,$y2,0,$backup2,$shade); /* ligne du haut, backup inutile */
- $this->aaborder($x2,$y2+$e,$ox,$oy+$e,1,$backup2,$shade);/* ligne du bas */
-
- if ($x2-$ox>1)
- imagefilltoborder($this->img,round(($ox+$x2)/2),round(($oy+$y2+$e)/2),$shade,$shade);
- $this->aaborder($x2,$y2+$e,$ox,$oy+$e,2,$backup2); /* aa ligne du bas */
- if ($v2<90) imageline($this->img,$x2,$y2,$x2,$y2+$e,$color); /* séparation verticale */
- $panel2=true;
- }
- if ($panel1 && $panel2) imageline($this->img,$ox,$oy,$ox,$oy+$e,$color);/* séparation verticale */
- }
-
- /* dessine le dessus de la portion */
- for ($k=$index1,$backup0=array();$k<=$index2;$k++) {
- $xk=$ox+$this->ellipse[$k]['x'];
- $yk=$oy+$this->ellipse[$k]['y'];
- $backup0[]=imagecolorat($this->img,$xk,$yk);
- imagesetpixel($this->img,$xk,$yk,$color);
- }
- /* tracé des "rayons", sauvegarde parfois inutile, mais bon... */
- $this->aaborder($ox,$oy,$x1,$y1,1,$backup1,$color); /* centre -> extrémité */
- $this->aaborder($x2,$y2,$ox,$oy,1,$backup2,$color); /* extrémité -> centre */
- /* "remplissage" du secteur en partant du "germe" */
- imagefilltoborder($this->img,$gx,$gy,$color,$color);
- /* correction du défaut du "secteur trop étroit" (pas toujours nécessaire) */
- imageline($this->img,$ox,$oy,$gx,$gy,$color);
- /* antialiasing secteur ellipse du haut */
- $this->aaarc($index1,$index2,$ox,$oy,$backup0);
- /* antialiasing des "rayons" du secteur elliptique */
- if ($xploded || $flag) {
- $this->aaborder($ox,$oy,$x1,$y1,2,$backup1); /* centre -> extrémité */
- $this->aaborder($x2,$y2,$ox,$oy,2,$backup2); /* extrémité -> centre */
- }
- else if ($i) { /* centre -> extrémité */
- if ($i<$this->pivot) $this->aaborder($ox,$oy,$x1,$y1,2,$backup1);
- else { /* extrémité -> centre */
- $this->aaborder($x2,$y2,$ox,$oy,2,$backup2);
- if ($i==$m-1) $this->aaborder($ox,$oy,$x1,$y1,2,$backup1);
- }
- }
-
- /* traitement des légendes */
- $ka=$AA*$py*$py+$BB*$px*$px;
- $kb=$AA*$dy*$px*$py;
- $kc=$AA*$px*$px*($dy*$dy-$BB);
-
- if ($vm<=90 || $vm>270) {
- $root=(-$kb+sqrt($kb*$kb-$ka*$kc))/$ka;
- $qx=round($this->cx+$root);
- $qy=round($this->cy+$root*$py/$px);
-
- $this->aaline($gx,$gy,$qx,$qy);
- $this->aaline($qx+1,$qy,$qx+10,$qy);
- imagestring($this->img,$fontsize,$qx+14,$qy-12,$this->data[$j]['legend'],$textcolor);
- imagestring($this->img,$fontsize,$qx+14,$qy-2,$this->data[$j]['value'],$textcolor);
- }
- else {
- $root=(-$kb-sqrt($kb*$kb-$ka*$kc))/$ka;
- $qx=round($this->cx+$root);
- $qy=round($this->cy+$root*$py/$px);
-
- $this->aaline($gx,$gy,$qx,$qy);
- $this->aaline($qx-1,$qy,$qx-10,$qy);
- $legendposx=$qx-12-$ifw*strlen($this->data[$j]['legend']);
- $valueposx=$qx-12-$ifw*strlen($this->data[$j]['value']);
- imagestring($this->img,$fontsize,$legendposx,$qy-12,$this->data[$j]['legend'],$textcolor);
- imagestring($this->img,$fontsize,$valueposx,$qy-2,$this->data[$j]['value'],$textcolor);
- }
- }
- }
-
- /* dessine un arc avec l'aa ---------------------------------------------------------------------------*/
-
- private function aaarc($begin,$end,$dx,$dy,&$backup)
- {
- for ($j=$begin,$i=0;$j<=$end;$j++,$i++) {
- list($red,$green,$blue)=$this->hex2rgb($backup[$i]);
- $mix=imagecolorexactalpha($this->img,$red,$green,$blue,$this->ellipse[$j]['alpha']);
- imagesetpixel($this->img,$dx+$this->ellipse[$j]['x'],$dy+$this->ellipse[$j]['y'],$mix);
- }
- }
-
- /* calcul de la transparence du point, intégration de l'enregistrement au tableau ---------------------*/
-
- private function add($x,$y)
- { /* 9 échantillons sont suffisants */
- for ($f=$x-0.2,$g1=$y-0.2,$g3=$y+0.2,$alpha=0;$f<=$x+0.2;$f+=0.2) {
- $k=$this->aabb-$this->bb*$f*$f;
- for ($g=$g1;$g<=$g3;$g+=0.2) if ($this->aa*$g*$g<$k) $alpha++;
- }
- return array('x'=>$x,'y'=>$y,'alpha'=>11*$alpha+28);
- }
-
- /* calcul d'un quart de l'ellipse, généralisation à l'ellipse entière ---------------------------------*/
-
- private function aaellipse()
- {
- $temp=array();
- /* algorithme volontairement non optimisé, pour ne pas le rendre encore plus hermétique ! */
- for ($x=0,$y=$this->b,$e=4*$this->bb+$this->aa*(1-4*$y);$this->bb*$x<$this->aa*$y;$x++) {
- $temp[2][]=$this->add($x,$y); /* octant n°2 */
- $e+= ($e<0)? $this->bb*(8*$x+12) : $this->bb*(8*$x+12)+8*$this->aa*(1-$y--);
- }
-
- for ($x=$this->a,$y=0,$e=4*$this->aa+$this->bb*(1-4*$x);$this->aa*$y<$this->bb*$x;$y++) {
- $temp[1][]=$this->add($x,$y); /* octant n° 1 */
- $e+= ($e<0)? $this->aa*(8*$y+12) : $this->aa*(8*$y+12)+8*$this->bb*(1-$x--);
- }
- /* on remet tout dans l'ordre qui convient */
- $this->ellipse=array_merge($temp[1],array_reverse($temp[2]));
- unset($temp);
- /* généralisation aux 3 autres quadrants */
- for ($i=0,$n=count($this->ellipse),$p=2*$n-2,$q=4*$n-4;$i<$n;$i++) {
- $x=$this->ellipse[$i]['x'];
- $y=$this->ellipse[$i]['y'];
- $alpha=$this->ellipse[$i]['alpha'];
- $this->ellipse[$p-$i]=array('x'=>-$x,'y'=>$y,'alpha'=>$alpha);
- $this->ellipse[$p+$i]=array('x'=>-$x,'y'=>-$y,'alpha'=>$alpha);
- $this->ellipse[$q-$i]=array('x'=>$x,'y'=>-$y,'alpha'=>$alpha);
- }
- $this->quarter=(count($this->ellipse)-1)/4;
- }
-
- /* fin de la classe -----------------------------------------------------------------------------------*/
-
- }
-
- $title='CodeS-SourceS : 33620 sources publiées!';
-
- $arr=array(
- /* donnée couleur légende xploded */
- array(2072, 0x969696, 'Divers', 0),
- array(1003, 0xFF9900, 'JAVA/J2EE', 0),
- array(1038, 0xFFCB03, 'C#', 0),
- array(1623, 0x99CC00, 'Flash', 0),
- array(1854, 0x339966, 'IRC', 0),
- array(1873, 0x33CCCC, 'Javascript/DHTML', 0),
- array(2208, 0x0091C3, 'PHP', 1),
- array(2624, 0xAA44AA, 'Delphi', 0),
- array(4971, 0xFF99CC, 'C/C++', 0),
- array(14354, 0xFF4C4C, 'Visual Basic', 0)
- );
-
- $mon_caprice_des_dieux=new aacamembert($title,$arr);
- ?>
<?php
header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header ('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header ('Cache-Control: no-cache, must-revalidate');
header ('Pragma: no-cache');
/*
- Date de création : 27/01/2008
- Nom : aacamembert.php, version simplifiée à "copier-coller"
- Auteur : opossum_farceur
- Object : Camembert, 3D, Antialiasing, Bresenham, Statistiques
*/
define('coef',M_PI/180);
class aacamembert
{
private $img;
private $a,$b,$cx,$cy,$aa,$bb,$aabb; /* ellipse */
private $data,$sort,$ellipse; /* tableaux */
private $quarter,$pivot; /* autres */
private $num,$linecolor; /* algorithme de Gupta-Sproull */
/* constructeur ---------------------------------------------------------------------------------------*/
function __construct($title,$arr)
{
$width=500;
$head=35; /* 15+15+5 : réservés pour le titre */
$height=300;
$w=240; /* grand axe horizontal */
$h=$w/2; /* 120 : petit axe vertical */
$thickness=$w/4; /* 60 : "épaisseur" du camembert */
$topcolor=0xFFFF00; /* dégradé : couleur du haut */
$bottomcolor=0x00FFFF; /* dégradé : couleur du bas */
$fontsize=3;
/* attributs calculés */
$this->a=$w/2; /* 1/2 grand axe : 120 */
$this->b=$h/2; /* 1/2 petit axe : 60 */
$this->cx=$width/2; /* abscisse et ordonnée du "centre" du camembert */
$this->cy=($height+$head-$thickness)/2;
$this->aa=$this->a*$this->a;
$this->bb=$this->b*$this->b;
$this->aabb=$this->aa*$this->bb;
/* attributs renseignés en cours de route */
$this->data=array();
$this->sort=array();
$this->ellipse=array();
/* antialiasing ligne droite */
$this->num=10; /* à choisir pair */
$this->linecolor=array(); /* taille du tableau : 1+3/2*$this->num, soit 16 (suffisant) */
/* préliminaires */
$this->img=imagecreatetruecolor($width,$height);
$this->mybackground($width-1,$height-1,$topcolor,$bottomcolor);
$textcolor=imagecolorallocate($this->img,0x00,0x00,0x00);
imagestring($this->img,5,($width-imagefontwidth(5)*strlen($title))/2,15,$title,$textcolor);
$this->aaellipse();
$this->gupta_sproull();
$specialcase=$this->init($arr,$thickness);
/* tracés */
$this->aadraw($thickness,$fontsize,$textcolor,$specialcase);
/* affichage */
header('Content-type: image/png');
imagepng($this->img);
imagedestroy($this->img);
}
/* "splite" une couleur en ses 3 composantes ----------------------------------------------------------*/
private function hex2rgb($hex)
{
return array(($hex>>16)& 0xFF,($hex>>8)& 0xFF,$hex & 0xFF);
}
/* mise en place du fond ------------------------------------------------------------------------------*/
private function mybackground($w,$h,$topcolor,$bottomcolor)
{
list($red,$green,$blue)=$this->hex2rgb($topcolor);
$bc=$this->hex2rgb($bottomcolor);
$incr=($bc[0]-$red)/$h;
$incg=($bc[1]-$green)/$h;
$incb=($bc[2]-$blue)/$h;
for($j=0;$j<=$h;$j++,$red+=$incr,$green+=$incg,$blue+=$incb)
imageline($this->img,0,$j,$w,$j,imagecolorallocate($this->img,$red,$green,$blue));
}
/* à partir de l'angle donné en argument retourne l'index correspondant du tableau de l'ellipse -------*/
private function index($v)
{ /* formulation du sinus à partir du cosinus, en principe + rapide */
$cosv=cos(coef*$v);
$sinv= ($v>180)? -sqrt(1-$cosv*$cosv) : sqrt(1-$cosv*$cosv);
$x=round($this->a*abs($cosv));
$y=round($this->b*abs($sinv));
$case=floor($v/90);
$p=($case+$case%2)*$this->quarter;
$sign= ($case%2)? -1 : 1;
$q= ($this->aa*$y-$this->bb*$x<0)? $y : $this->quarter-$x;
return $p+$sign*$q;
}
/* traitement des données fournies dans le tableau ----------------------------------------------------*/
private function init($arr,$e)
{ /* calcul de la somme des données */
for ($j=$sum=0,$n=count($arr);$j<$n;$j++) $sum+=$arr[$j][0];
/* analyse du tableau */
for ($j=$k=$v1=0;$j<$n;$j++) if ($arr[$j][0]) { /* cas non traité si donnée nulle */
/* extraction des composantes red, green et blue */
list($red,$green,$blue)=$this->hex2rgb($arr[$j][1]);
$this->data[$k]['color']=imagecolorallocate($this->img,$red,$green,$blue);
/* traitement des angles */
$v2=$v1+$arr[$j][0]*360/$sum;
if ($v1<=270 && $v2>=270) $first=$k;
if ($v1<=90 && $v2>=90) $last=$k;
$this->data[$k]['v']=$v2;
/* détermine la couleur ombrée */
$this->data[$k]['shade']=imagecolorallocate($this->img,max(0,$red-50),max(0,$green-50),max(0,$blue-50));
/* préparation du texte */
$this->data[$k]['legend']=$arr[$j][2];
$this->data[$k]['value']=number_format(100*$arr[$j][0]/$sum,2,',','').'%';
$this->data[$k]['xploded']=$arr[$j][3];
$v1=$v2;
$k++;
}
$m=count($this->data); /* différent de $n si une ou plusieurs donnée(s) sont nulles */
if ($first!=$last) {
for ($j=0,$k=$first;$k!=$last;$j++,$k=($k+1)%$m) $this->sort[$j]=$k;
$this->pivot=$j;
for ($k=$first-1;$k>=$last;$j++,$k--) $this->sort[$j]=$k;
return false;
}
else { /* cas particulier préoccupant : une grosse portion présente à la fois à 12h et à 18h */
for ($j=0,$k=($first+1)%$m;$j<$m;$j++,$k=($k+1)%$m) $this->sort[$j]=$k;
$this->pivot=$m-1;
/* mise en place du 2ème panneau de la dernière portion (origine du code : méthode "aadraw()") */
$j=$this->sort[$m-1];
$xploded=$this->data[$j]['xploded'];
if ($this->data[$this->sort[0]]['xploded'] || $xploded) {
$shade=$this->data[$j]['shade'];
$v1= ($j)? $this->data[$j-1]['v'] : 0;
$v2=$this->data[$j]['v'];
$vm=($v1+$v2)/2;
if ($xploded) {
$ox=round($this->cx+$this->a*cos(coef*$vm)/6); /* a/6, b/6 : "offsets" des portions */
$oy=round($this->cy+$this->b*sin(coef*$vm)/6);
}
else {
$ox=$this->cx;
$oy=$this->cy;
}
$index2=$this->index($v2);
$x2=$ox+$this->ellipse[$index2]['x'];
$y2=$oy+$this->ellipse[$index2]['y'];
imageline($this->img,$ox,$oy,$ox,$oy+$e,$shade);
imageline($this->img,$x2,$y2,$x2,$y2+$e,$shade);
$this->aaborder($ox,$oy,$x2,$y2,0,$backup2,$shade); /* ligne du haut */
$this->aaborder($x2,$y2+$e,$ox,$oy+$e,1,$backup2,$shade);/* ligne du bas (attention à l'ordre!) */
if ($x2-$ox>1) imagefilltoborder($this->img,round(($ox+$x2)/2),round(($oy+$y2+$e)/2),$shade,$shade);
$this->aaborder($x2,$y2+$e,$ox,$oy+$e,2,$backup2); /* aa ligne du bas */
}
return true;
}
}
/* calcul des 16 tons de gris utiles au dessin des lignes droites antialiassées -----------------------*/
private function gupta_sproull()
{ /* cette fonction crée une table allant de 0 à 1.5*$this->num */
$col=array();
$numdiv2=$this->num/2;
/* calcul du volume interceptant chaque colonne, i et j parcourent un quart du cône */
for ($i=$this->num,$total=0;$i>=0;$i--) {
for ($j=$this->num,$k=0,$ii=$i*$i;$j>=0;$j--) {
$d=sqrt($ii+$j*$j);
if ($d<$this->num) $k+=$this->num-$d;
}
$col[$this->num-$i]=2*$k;
$total+=4*$k;
}
/* calcul de la table : 1ere partie correspondant à une intersection d'épaisseur inférieure à 1 */
$max=$this->num+$numdiv2;
for ($i=$k=0;$i<=$this->num;$i++) {
$k+=$col[$i];
$this->linecolor[$max-$i]=imagecolorexactalpha($this->img,0,0,0,round(127*(1-$k/$total)));
}
/* calcul de la table : 2eme partie correspondant à une intersection d'épaisseur supérieure à 1 */
for ($i=1;$i<=$numdiv2;$i++) {
$k+=$col[$this->num-$i]-$col[$i];
$this->linecolor[$numdiv2-$i]=imagecolorexactalpha($this->img,0,0,0,round(127*(1-$k/$total)));
}
}
/* "allume" le pixel avec une intensité fonction de la distance à la droite ---------------------------*/
private function plot($x,$y,$d)
{
if ($d<0) $d=-$d;
if ($d>1.5) return; /* rien à allumer si le pixel est trop loin */
imagesetpixel($this->img,$x,$y,$this->linecolor[round($d*$this->num)]);
}
/* tracé de droite "antialiassée" ---------------------------------------------------------------------*/
private function aaline($x1,$y1,$x2,$y2)
{
$dx=abs($x2-$x1);
$dy=abs($y2-$y1);
$incj= (($x2-$x1)*($y2-$y1)>0)? 1 : -1;
$K=1/(2*sqrt($dx*$dx+$dy*$dy));
if ($dx>$dy) { /* tendance "horizontale" */
if ($x2>$x1) {
$y=$y1;
$begin=$x1;
$end=$x2;
}
else {
$y=$y2;
$begin=$x2;
$end=$x1;
}
$p=2*$dy;
$delta=$dx;
$incx=0;
$incy=$incj;
$i=&$x;
$j=&$y;
}
else { /* tendance "verticale" */
if ($y2>$y1) {
$x=$x1;
$begin=$y1;
$end=$y2;
}
else {
$x=$x2;
$begin=$y2;
$end=$y1;
}
$p=2*$dx;
$delta=$dy;
$incx=$incj;
$incy=0;
$i=&$y;
$j=&$x;
}
/* tronc commun */
for ($i=$begin,$e=$p-$delta,$q=$e-$delta,$D=0,$R=2*$K*$delta;$i<=$end;$i++) {
$this->plot($x,$y,$D);
$this->plot($x+$incx,$y+$incy,$R-$D);
$this->plot($x-$incx,$y-$incy,$R+$D);
if ($e>=0) {
$D=$K*($e-$delta);
$j+=$incj;
$e+=$q;
}
else {
$D=$K*($e+$delta);
$e+=$p;
}
}
}
/* traitement de l'aa des "bordures" ------------------------------------------------------------------*/
private function aaborder($x1,$y1,$x2,$y2,$mode,&$backup,$mycolor=0)
{
$dx=$x2-$x1;
$dy=$y2-$y1;
$incj= ($dx*$dy>0)? 1 : -1;
if ($mode==1) $backup=array();
elseif ($mode==2) $const=$dx*$y1-$dy*$x1;
if (abs($dx)>abs($dy)) { /* tendance "horizontale" */
if ($dx>0) {
$y=$y1;
$begin=$x1;
$end=$x2;
}
else {
$y=$y2;
$begin=$x2;
$end=$x1;
}
$p=2*abs($dy);
$e=$p-abs($dx);
$q=$e-abs($dx);
$i=&$x;
$j=&$y;
}
else { /* tendance "verticale" */
if ($dy>0) {
$x=$x1;
$begin=$y1;
$end=$y2;
}
else {
$x=$x2;
$begin=$y2;
$end=$y1;
}
$p=2*abs($dx);
$e=$p-abs($dy);
$q=$e-abs($dy);
$i=&$y;
$j=&$x;
}
/* tronc commun */
for ($i=$begin,$t=0;$i<=$end;$i++,$t++) {
if ($mode==1) $backup[]=imagecolorat($this->img,$x,$y); /* mode "sauvegarde" */
elseif ($mode==2) { /* mode "antialiasing" */
for ($f=$x-0.2,$g1=$y-0.2,$g3=$y+0.2,$alpha=0;$f<=$x+0.2;$f+=0.2) {
$k=$const+$dy*$f;
for ($g=$g1;$g<=$g3;$g+=0.2) if ($dx*$g>$k) $alpha++;
}
list($red,$green,$blue)=$this->hex2rgb($backup[$t]);
$mycolor=imagecolorexactalpha($this->img,$red,$green,$blue,11*$alpha+28);
}
imagesetpixel($this->img,$x,$y,$mycolor);
if ($e>=0) {
$j+=$incj;
$e+=$q;
}
else $e+=$p;
}
}
/* tracé du camembert ---------------------------------------------------------------------------------*/
private function aadraw($e,$fontsize,$textcolor,$specialcase)
{
$A=$this->a*21/16; /* 1/2 grand axe de l'ellipse "englobante" */
$B=3*$A/4; /* bien que dépendant de $e, mais bon... */
$dy=-$e/2; /* "centre" du camembert à celui de l'ellipse "englobante" */
$AA=$A*$A;
$BB=$B*$B;
$ifw=imagefontwidth($fontsize);
/* "grande boucle" */
for ($i=0,$m=count($this->data);$i<$m;$i++) {
$j=$this->sort[$i];
/* initialisations */
$color=$this->data[$j]['color'];
$shade=$this->data[$j]['shade'];
$v1= ($j)? $this->data[$j-1]['v'] : 0;
$v2=$this->data[$j]['v'];
$vm=($v1+$v2)/2;
$index1=$this->index($v1);
$index2=$this->index($v2);
$xploded=$this->data[$j]['xploded'];
$panel1=$panel2=false;
$flag=0;
if ($xploded) {
$px=$this->a*cos(coef*$vm);
$py=$this->b*sin(coef*$vm);
$ox=round($this->cx+$px/6); /* a/6, b/6 : "offsets" des portions */
$oy=round($this->cy+$py/6);
}
else {
$px=5*$this->a*cos(coef*$vm)/6;
$py=5*$this->b*sin(coef*$vm)/6;
$ox=$this->cx;
$oy=$this->cy;
switch ($i) { /* cumulations possibles */
case 0: $flag=$this->data[$this->sort[$this->pivot]]['xploded'];
case $this->pivot-1: $flag|=$this->data[$this->sort[$m-1]]['xploded'];
default: $flag|=$this->data[$this->sort[$i+1]]['xploded'];
}
}
$x1=$ox+$this->ellipse[$index1]['x'];
$y1=$oy+$this->ellipse[$index1]['y'];
$x2=$ox+$this->ellipse[$index2]['x'];
$y2=$oy+$this->ellipse[$index2]['y'];
$gx=round($this->cx+$px); /* sans arrondi, problème raccordement segment en size 1 */
$gy=round($this->cy+$py);
/* traitement secteur du bas + paravent vertical */
if ($v1<180) {
if ($v2<180) {
$indexmax=$index2;
$x=$x2;
$y=$y2;
}
else { /* le traitement s'arrête à 180° */
$indexmax=2*$this->quarter; /* aussi : $this->index(180); */
$x=$ox-$this->a; /* aussi : $ox+$this->ellipse[$indexmax]['x']; */
$y=$oy; /* aussi : $oy+$this->ellipse[$indexmax]['y'] */
}
/* dessin des secteurs hauts et bas de l'ellipse */
for ($k=$index1,$backup0=array();$k<=$indexmax;$k++) {
$xk=$ox+$this->ellipse[$k]['x'];
$yk=$oy+$this->ellipse[$k]['y'];
$backup0[]=imagecolorat($this->img,$xk,$yk+$e);
imagesetpixel($this->img,$xk,$yk,$shade);
imagesetpixel($this->img,$xk,$yk+$e,$shade);
}
/* lignes verticales */
imageline($this->img,$x1,$y1,$x1,$y1+$e,$shade);
imageline($this->img,$x,$y,$x,$y+$e,$shade);
/* "remplissage" peu optimisé mais valable dans tous les cas de figures */
if ($x1-$x>1) imagefilltoborder($this->img,$x+1,$y+$e/2,$shade,$shade);
/* antialiasing secteur ellipse du bas */
$this->aaarc($index1,$indexmax,$ox,$oy+$e,$backup0);
}
if ($xploded || $flag) { /* dessine les "panneaux" verticaux */
if ($v1>90 && $v1<270) {
imageline($this->img,$ox,$oy,$ox,$oy+$e,$shade);
imageline($this->img,$x1,$y1,$x1,$y1+$e,$shade); /* parfois inutile, mais bon... */
$this->aaborder($x1,$y1,$ox,$oy,0,$backup1,$shade); /* ligne du haut, backup inutile */
$this->aaborder($ox,$oy+$e,$x1,$y1+$e,1,$backup1,$shade);/* ligne du bas, backup parfois inutile */
if ($ox-$x1>1)
imagefilltoborder($this->img,round(($ox+$x1)/2),round(($oy+$y1+$e)/2),$shade,$shade);
$this->aaborder($ox,$oy+$e,$x1,$y1+$e,2,$backup1); /* aa ligne du bas */
if ($v1<180) imageline($this->img,$x1,$y1,$x1,$y1+$e,$color); /* séparation verticale */
$panel1=true;
}
if ($v2<90 ||($v2>270 &&!($specialcase && $i==$m-1))) { /* $specialcase n'apparait qu'ici */
imageline($this->img,$ox,$oy,$ox,$oy+$e,$shade);
imageline($this->img,$x2,$y2,$x2,$y2+$e,$shade);
$this->aaborder($ox,$oy,$x2,$y2,0,$backup2,$shade); /* ligne du haut, backup inutile */
$this->aaborder($x2,$y2+$e,$ox,$oy+$e,1,$backup2,$shade);/* ligne du bas */
if ($x2-$ox>1)
imagefilltoborder($this->img,round(($ox+$x2)/2),round(($oy+$y2+$e)/2),$shade,$shade);
$this->aaborder($x2,$y2+$e,$ox,$oy+$e,2,$backup2); /* aa ligne du bas */
if ($v2<90) imageline($this->img,$x2,$y2,$x2,$y2+$e,$color); /* séparation verticale */
$panel2=true;
}
if ($panel1 && $panel2) imageline($this->img,$ox,$oy,$ox,$oy+$e,$color);/* séparation verticale */
}
/* dessine le dessus de la portion */
for ($k=$index1,$backup0=array();$k<=$index2;$k++) {
$xk=$ox+$this->ellipse[$k]['x'];
$yk=$oy+$this->ellipse[$k]['y'];
$backup0[]=imagecolorat($this->img,$xk,$yk);
imagesetpixel($this->img,$xk,$yk,$color);
}
/* tracé des "rayons", sauvegarde parfois inutile, mais bon... */
$this->aaborder($ox,$oy,$x1,$y1,1,$backup1,$color); /* centre -> extrémité */
$this->aaborder($x2,$y2,$ox,$oy,1,$backup2,$color); /* extrémité -> centre */
/* "remplissage" du secteur en partant du "germe" */
imagefilltoborder($this->img,$gx,$gy,$color,$color);
/* correction du défaut du "secteur trop étroit" (pas toujours nécessaire) */
imageline($this->img,$ox,$oy,$gx,$gy,$color);
/* antialiasing secteur ellipse du haut */
$this->aaarc($index1,$index2,$ox,$oy,$backup0);
/* antialiasing des "rayons" du secteur elliptique */
if ($xploded || $flag) {
$this->aaborder($ox,$oy,$x1,$y1,2,$backup1); /* centre -> extrémité */
$this->aaborder($x2,$y2,$ox,$oy,2,$backup2); /* extrémité -> centre */
}
else if ($i) { /* centre -> extrémité */
if ($i<$this->pivot) $this->aaborder($ox,$oy,$x1,$y1,2,$backup1);
else { /* extrémité -> centre */
$this->aaborder($x2,$y2,$ox,$oy,2,$backup2);
if ($i==$m-1) $this->aaborder($ox,$oy,$x1,$y1,2,$backup1);
}
}
/* traitement des légendes */
$ka=$AA*$py*$py+$BB*$px*$px;
$kb=$AA*$dy*$px*$py;
$kc=$AA*$px*$px*($dy*$dy-$BB);
if ($vm<=90 || $vm>270) {
$root=(-$kb+sqrt($kb*$kb-$ka*$kc))/$ka;
$qx=round($this->cx+$root);
$qy=round($this->cy+$root*$py/$px);
$this->aaline($gx,$gy,$qx,$qy);
$this->aaline($qx+1,$qy,$qx+10,$qy);
imagestring($this->img,$fontsize,$qx+14,$qy-12,$this->data[$j]['legend'],$textcolor);
imagestring($this->img,$fontsize,$qx+14,$qy-2,$this->data[$j]['value'],$textcolor);
}
else {
$root=(-$kb-sqrt($kb*$kb-$ka*$kc))/$ka;
$qx=round($this->cx+$root);
$qy=round($this->cy+$root*$py/$px);
$this->aaline($gx,$gy,$qx,$qy);
$this->aaline($qx-1,$qy,$qx-10,$qy);
$legendposx=$qx-12-$ifw*strlen($this->data[$j]['legend']);
$valueposx=$qx-12-$ifw*strlen($this->data[$j]['value']);
imagestring($this->img,$fontsize,$legendposx,$qy-12,$this->data[$j]['legend'],$textcolor);
imagestring($this->img,$fontsize,$valueposx,$qy-2,$this->data[$j]['value'],$textcolor);
}
}
}
/* dessine un arc avec l'aa ---------------------------------------------------------------------------*/
private function aaarc($begin,$end,$dx,$dy,&$backup)
{
for ($j=$begin,$i=0;$j<=$end;$j++,$i++) {
list($red,$green,$blue)=$this->hex2rgb($backup[$i]);
$mix=imagecolorexactalpha($this->img,$red,$green,$blue,$this->ellipse[$j]['alpha']);
imagesetpixel($this->img,$dx+$this->ellipse[$j]['x'],$dy+$this->ellipse[$j]['y'],$mix);
}
}
/* calcul de la transparence du point, intégration de l'enregistrement au tableau ---------------------*/
private function add($x,$y)
{ /* 9 échantillons sont suffisants */
for ($f=$x-0.2,$g1=$y-0.2,$g3=$y+0.2,$alpha=0;$f<=$x+0.2;$f+=0.2) {
$k=$this->aabb-$this->bb*$f*$f;
for ($g=$g1;$g<=$g3;$g+=0.2) if ($this->aa*$g*$g<$k) $alpha++;
}
return array('x'=>$x,'y'=>$y,'alpha'=>11*$alpha+28);
}
/* calcul d'un quart de l'ellipse, généralisation à l'ellipse entière ---------------------------------*/
private function aaellipse()
{
$temp=array();
/* algorithme volontairement non optimisé, pour ne pas le rendre encore plus hermétique ! */
for ($x=0,$y=$this->b,$e=4*$this->bb+$this->aa*(1-4*$y);$this->bb*$x<$this->aa*$y;$x++) {
$temp[2][]=$this->add($x,$y); /* octant n°2 */
$e+= ($e<0)? $this->bb*(8*$x+12) : $this->bb*(8*$x+12)+8*$this->aa*(1-$y--);
}
for ($x=$this->a,$y=0,$e=4*$this->aa+$this->bb*(1-4*$x);$this->aa*$y<$this->bb*$x;$y++) {
$temp[1][]=$this->add($x,$y); /* octant n° 1 */
$e+= ($e<0)? $this->aa*(8*$y+12) : $this->aa*(8*$y+12)+8*$this->bb*(1-$x--);
}
/* on remet tout dans l'ordre qui convient */
$this->ellipse=array_merge($temp[1],array_reverse($temp[2]));
unset($temp);
/* généralisation aux 3 autres quadrants */
for ($i=0,$n=count($this->ellipse),$p=2*$n-2,$q=4*$n-4;$i<$n;$i++) {
$x=$this->ellipse[$i]['x'];
$y=$this->ellipse[$i]['y'];
$alpha=$this->ellipse[$i]['alpha'];
$this->ellipse[$p-$i]=array('x'=>-$x,'y'=>$y,'alpha'=>$alpha);
$this->ellipse[$p+$i]=array('x'=>-$x,'y'=>-$y,'alpha'=>$alpha);
$this->ellipse[$q-$i]=array('x'=>$x,'y'=>-$y,'alpha'=>$alpha);
}
$this->quarter=(count($this->ellipse)-1)/4;
}
/* fin de la classe -----------------------------------------------------------------------------------*/
}
$title='CodeS-SourceS : 33620 sources publiées!';
$arr=array(
/* donnée couleur légende xploded */
array(2072, 0x969696, 'Divers', 0),
array(1003, 0xFF9900, 'JAVA/J2EE', 0),
array(1038, 0xFFCB03, 'C#', 0),
array(1623, 0x99CC00, 'Flash', 0),
array(1854, 0x339966, 'IRC', 0),
array(1873, 0x33CCCC, 'Javascript/DHTML', 0),
array(2208, 0x0091C3, 'PHP', 1),
array(2624, 0xAA44AA, 'Delphi', 0),
array(4971, 0xFF99CC, 'C/C++', 0),
array(14354, 0xFF4C4C, 'Visual Basic', 0)
);
$mon_caprice_des_dieux=new aacamembert($title,$arr);
?>
Historique
- 02 octobre 2006 00:40:41 :
- Correction du bug signalé par CHPITIT01 (seulement dans le zip !)
Optimisation du code
- 15 février 2007 20:44:52 :
- correction d'un bug signalé par GUISMO1ER, CHRIS_TOPHE2, EZA, VISTUO, concernant la couleur des très petites portions qui pouvait dans certains cas envahir tout le camembert.
- 04 mars 2007 02:32:27 :
- Ce nouveau code est une généralisation de "aacamembertplus" avec un camembert qui peut désormais interagir avec un fond "quelconque" : le dispositif est constitué d'une classe abstraite "phpmycamembert" dont dérivent les classes "aabasic" (fond uni), "aagradient" (dégradé entre 2 couleurs) et "aaimage" (image de fond).
Note1 : le code est en php4 pour une compatibilité maximale.
Note2 : j'ai supprimé du zip les documents récupérés sur internet; des liens vers leurs sites d'origine permettent cependant de les retrouver sans problèmes.
- 26 mars 2007 20:36:55 :
- version 3.0
- Le constructeur de la classe de base "phpmycamembert" admet désormais un paramètre
supplémentaire, le booléen "$xploded"; il suffira que ce paramètre soit à "true" pour éclater le camembert.
- Regroupement de toutes les classes dans un fichier unique "camembert.php". Le dispositif,
reposant désormais sur un "switch", est moins conventionnel, mais permet d'éviter la
multiplication de fichiers avec 3 lignes de code dedans.
- 08 avril 2007 00:01:35 :
- version 3.1 : correction bug signalé par SKALASKA, concernant les traits de légende des camemberts "condensés".
- 08 avril 2007 14:19:20 :
- La mise à jour précédente n'ayant pas fonctionné (encore les mystères de l'informatique!), je l'exécute une deuxième fois...
- 02 février 2008 00:18:07 :
- version 4.0
- traitement individuel de l'explosion de chaque part
- migration vers php5
- adaptation du code à une page appelante en "ajax"
- 29 septembre 2008 01:35:38 :
- version 4.1 : amélioration/consolidation/optimisation du code existant, ajout des dégradés dans les parties verticales courbes du camembert, simplification de l'exploitation "ajax" de la classe.
Sources du même auteur
Sources de la même categorie
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
faire un graphique camembert 3D, a partir d'une base de données [ par otacon67 ]
je suis un newbies en PHP, et je voudrais savoir comment creer un camembert en 3D, a partir d'une base de données merci pour votre soutien et vo
Rafraichir une page [ par niko14 ]
Hello everybody!!Voila le bin's!!En fait voila, j'ai une page ou on choisit un type de budget et on valide!Qd on a validé on arrive sur une page
tableau de couleurs comme dans word [ par niko14 ]
slt tout le monde!VOila mon probleme!J'ai un camembert donc je fais choisir les couleurs pour les differentes repartitions et en fait j'avais fait une
statistiques : temps passé sur une page [ par Phil0 ]
Bonjour, Je suis actuellement entrain de faire un script php de statistiques et je voudrais, si c'est possible, savoir comment connaitre le temps pass
statistiques [ par gabs77 ]
bonjour, je cherche a faire des statistiques numériques avec un formulaire de choix des informations a évaluerdonc les statistiques se feront sur la t
Statistiques d'une newsletter [ par blaize21 ]
Bonjour! Je viens de réaliser un script plutôt basique d'envoi de newsletter et je voudrais obtenir les statistiques de lecture de ma newsletter. J'a
Statistiques [ par Sniperman113 ]
Bonjour,Je voudrai créer un script de statistiques (notamment bénéfices des 30 derniers jours) sous forme graphique. Par exemple dans l'idéal, il faud
générer des statistiques graphiques a partir d'un fichier xml [ par ammar_emi ]
bonjour,je suis débutant en xml ,et je vx générer des statistiques graphiques d'un user d' une plateforme.J ai reussi à extraire les infos utiles pour
générer des statistiques [ par charbouba ]
svp comment utiliser gd pour tracé des graphiques a partir d'un fichier xmlsi qlq a un exemple ca serait mieux:) .merci.(c urgent)
Librairie traceur fonction 3D [ par Jarod1980 ]
Bonjour,Je souhaiterais si il existe une librairie en php permettant de tracer des fonctions mathématiques en 3D. J'ai fais une recherche sur net mais
|
Derniers Blogs
L'INTERFACE NATURELLE DE WINDOWS PHONE 7 SERIESL'INTERFACE NATURELLE DE WINDOWS PHONE 7 SERIES par odewit
La tendance est aux interfaces naturelles (NUI), et le keynote de Bill Buxton au MIX l'a bien souligné.
La charte graphique et ergonomique de Windows Phone 7 a donc été entièrement repensée en vue d'obtenir un maximum d'efficacité sur ce point. En re...
Cliquez pour lire la suite de l'article par odewit COMMENT MAPPER UNE VUE SQL SUR UNE COLLECTION DE COMPLEX TYPE?COMMENT MAPPER UNE VUE SQL SUR UNE COLLECTION DE COMPLEX TYPE? par Matthieu MEZIL
Avec EF, les vues doivent être mappées sur des entity types. Le problème c'est que les entity types doivent avoir une clé. Avec EF, nous avons les complex type qui n'ont pas de clé mais les vues ne peuvent pas être mappées dessus. Avec EF4, il est possibl...
Cliquez pour lire la suite de l'article par Matthieu MEZIL [WF4] UN BINDING ACTIVITY/ACTIVITYDESIGNER QUI PASSE MAL?[WF4] UN BINDING ACTIVITY/ACTIVITYDESIGNER QUI PASSE MAL? par JeremyJeanson
Certain d'entre vous on peut être vécu cette situation embarrassante après quelques temps passer avec WF4 : Au début avec mon " ActivityDesigner" , tout allait bien. Et puis un jour j'ai au des problèmes de " Binding" . Alors nous sommes allé sur le site ...
Cliquez pour lire la suite de l'article par JeremyJeanson
Logiciels
Academy System (10.9.4.0)ACADEMY SYSTEM (10.9.4.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System Xilisoft Convertisseur Vidéo Ultimate (5.1.39.0305)XILISOFT CONVERTISSEUR VIDéO ULTIMATE (5.1.39.0305)Xilisoft Convertisseur Vidéo Ultimate est un outil puissant de conversion vidéo, facile à utilise... Cliquez pour télécharger Xilisoft Convertisseur Vidéo Ultimate Xilisoft DVD Ripper Ultimate (5.0.64.0304)XILISOFT DVD RIPPER ULTIMATE (5.0.64.0304)Xilisoft DVD Ripper Ultimate est un logiciel excellent pour copier et convertir DVD vers presque ... Cliquez pour télécharger Xilisoft DVD Ripper Ultimate Rigs of Rods (63.3)RIGS OF RODS (63.3)c'est un jeu de multi-simulation camions,autobus voitures, avions, bateaux, hélicoptère avec défo... Cliquez pour télécharger Rigs of Rods
|