]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/lib/CCircle.pm
88f7e188baa64c60445a753fbac103758afe1621
[bacula/bacula] / gui / bweb / lib / CCircle.pm
1 package CCircle ;
2
3 =head1 LICENSE
4
5    Bweb - A Bacula web interface
6    Bacula® - The Network Backup Solution
7
8    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
9
10    The main author of Bweb is Eric Bollengier.
11    The main author of Bacula is Kern Sibbald, with contributions from
12    many others, a complete list can be found in the file AUTHORS.
13    This program is Free Software; you can redistribute it and/or
14    modify it under the terms of version three of the GNU Affero General Public
15    License as published by the Free Software Foundation and included
16    in the file LICENSE.
17
18    This program is distributed in the hope that it will be useful, but
19    WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU Affero General Public License
24    along with this program; if not, write to the Free Software
25    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26    02110-1301, USA.
27
28    Bacula® is a registered trademark of Kern Sibbald.
29    The licensor of Bacula is the Free Software Foundation Europe
30    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
31    Switzerland, email:ftf@fsfeurope.org.
32
33 =cut
34
35 use strict ;
36 use GD ;
37
38 my $pi =  3.14159265;
39
40 our $gd ;
41 our @color_tab ; 
42
43 our $black ;
44 our $white ;
45
46 our $last_level = 1 ;
47 our @draw_label ; 
48 our $height ;
49 our $width ;
50
51 our $cur_color = 1 ;
52
53 my $debug = 0 ;
54 my $font_size = 6 ;
55
56 our @image_map ;
57
58 sub new
59 {
60     my ($class, %arg) = @_ ;
61
62     my $self = {
63         start_degrees    => 0,
64         degrees_complete => 0,
65         parent_percent   => 100,
66         level            => 1,
67         center_x         => 600,
68         center_y         => 300,
69         diameter         => 75,
70         percent_total    => 0,
71         min_percent      => 2,
72         min_total        => 0,
73         width            => 1200,
74         height           => 600,
75         min_label        => 4,
76         min_degrees      => 2,
77         max_label_level  => 2,
78         display_other    => 0,
79         base_url         => '',
80     } ;
81
82     map { $self->{$_} = $arg{$_} } keys %arg ;
83
84     unless(defined $gd) {
85         $height = $self->{height} ;
86         $width  = $self->{width} ;
87
88         $gd    = new GD::Image($width,$height);
89         $white = $gd->colorAllocate(255,255,255);
90
91         push @color_tab, ($gd->colorAllocate(135,236,88),
92                           $gd->colorAllocate(255,95,95),
93                           $gd->colorAllocate(245,207,91),
94                           $gd->colorAllocate( 255, 236, 139),
95                           $gd->colorAllocate( 255, 174, 185),
96                           $gd->colorAllocate( 179, 255,  58),
97                           $gd->colorAllocate( 205, 133,   0),
98                           $gd->colorAllocate(205, 133,   0 ),
99                           $gd->colorAllocate(238, 238, 209),
100
101                           ) ;
102                       
103
104         $black = $gd->colorAllocate(0,0,0);
105         #$gd->>transparent($white);
106         $gd->interlaced('true');
107
108         $gd->arc($self->{center_x},$self->{center_y}, 
109                  $self->{diameter},$self->{diameter},
110                  $self->{start_degrees},360, $black) ;
111
112     }
113
114     $self->{rayon} = $self->{diameter} / 2 ;
115
116     bless $self ;
117
118     # pour afficher les labels tout propre
119     if ($self->{level} > $last_level) {
120         $last_level = $self->{level} ;
121     }
122
123     return $self ;
124 }
125
126 sub calc_xy
127 {
128     my ($self, $level, $deg) = @_ ;
129
130     my $x1 =   $self->{center_x}+$self->{rayon} * $level * cos($deg*$pi/180) ;
131     my $y1 =   $self->{center_y}+$self->{rayon} * $level * sin($deg*$pi/180) ;
132
133     return ($x1, $y1) ;
134
135 }
136
137 sub add_part
138 {
139     my ($self, $percent, $label, $tips) = @_ ;
140
141     $tips = $tips || $label ;
142
143     if (($percent + $self->{percent_total}) > 100.05) {
144         print STDERR "Attention $label ($percent\% + $self->{percent_total}\%) > 100\%\n" ;
145         return undef; 
146     }
147
148     if ($percent <= 0) {
149         print STDERR "Attention $label <= 0\%\n" ;
150         return undef; 
151     }
152
153     # angle de depart de l'arc
154     my $start_degrees = (($self->{degrees_complete})?
155                          $self->{degrees_complete}:$self->{start_degrees}) ;
156
157     # angle de fin de l'arc
158     my $end_degrees = $start_degrees +
159         ($percent * ( ( $self->{parent_percent} * 360 )/100 ) ) /100 ;
160
161 #    print STDERR "-------- $debug --------
162 #percent = $percent%
163 #label = $label
164 #level = $self->{level}
165 #start = $start_degrees
166 #end   = $end_degrees
167 #parent= $self->{parent_percent}
168 #" ;
169     if (($end_degrees - $start_degrees) < $self->{min_degrees}) {
170         $self->{min_total} += $percent ;
171         return undef ;
172     }
173
174     if ($percent <= $self->{min_percent}) {
175         $self->{min_total} += $percent ;
176         return undef ;
177     }
178
179     # on totalise les % en cours
180     $self->{percent_total} += $percent ;
181
182     #print STDERR "percent_total = $self->{percent_total}\n" ;
183
184     # position dans le cercle
185     my $n = $self->{level} ; # on ajoute/retire 0.005 pour depasser un peu
186     my $m = $n+1 ;
187
188     # si c'est la premiere tranche de la nouvelle serie, il faut dessiner
189     # la premiere limite 
190
191     if ($self->{degrees_complete} == 0) {
192         my ($x1, $y1) = $self->calc_xy($n-0.005, $self->{start_degrees}) ;
193         my ($x2, $y2) = $self->calc_xy($m+0.005, $self->{start_degrees}) ;
194
195         $gd->line($x1, $y1, $x2, $y2, $black) ;
196     }
197
198     # seconde ligne
199     my ($x3, $y3) = $self->calc_xy($n-0.005, $end_degrees) ;
200
201     my ($x4, $y4) = $self->calc_xy($m+0.005, $end_degrees) ;
202
203     # ligne de bord exterieur
204     $gd->line($x3, $y3, $x4, $y4, $black);
205
206     # on dessine le bord
207     $gd->arc($self->{center_x},$self->{center_y}, 
208              $self->{diameter}*($self->{level}+1),
209              $self->{diameter}*($self->{level}+1),
210
211              $start_degrees-0.5,
212              $end_degrees+0.5, $black) ;
213
214     # on calcule le point qui est au milieu de la tranche
215     # angle = (angle nouvelle tranche)/2
216
217     # rayon = n*rayon - 0.5*rayon
218     # n=1 -> 0.5
219     # n=2 -> 1.5
220     # n=3 -> 2.5
221
222     my $mid_rad = ($end_degrees - $start_degrees) /2 + $start_degrees;
223
224     my $moy_x = ($self->{center_x}+
225                    ($self->{rayon}*$m - 0.5*$self->{rayon})
226                        *cos($mid_rad*$pi/180)) ;
227
228     my $moy_y = ($self->{center_y}+
229                    ($self->{rayon}*$m - 0.5*$self->{rayon})
230                        *sin($mid_rad*$pi/180)) ;
231
232     $gd->fillToBorder($moy_x, 
233                       $moy_y,
234                       $black,
235                       $cur_color) ;
236     
237     # on prend une couleur au hasard
238     $cur_color = ($cur_color % $#color_tab) + 1 ;
239
240     # si le % est assez grand, on affiche le label
241     if ($percent > $self->{min_label}) {
242         push @draw_label, [$label, 
243                            $moy_x, $moy_y, 
244                            $self->{level}] ;
245
246         $self->push_image_map($label, $tips, $start_degrees, $end_degrees) ;
247     }
248
249     # pour pourvoir ajouter des sous donnees
250     my $ret = new CCircle(start_degrees  => $start_degrees, 
251                           parent_percent => $percent*$self->{parent_percent}/100,
252                           level          => $m,
253                           center_x       => $self->{center_x},
254                           center_y       => $self->{center_y},
255                           min_percent    => $self->{min_percent},
256                           min_degrees    => $self->{min_degrees},
257                           base_url       => $self->{base_url} . $label,
258                           ) ;
259                  
260     $self->{degrees_complete} = $end_degrees ;
261     
262     #print STDERR "$debug : [$self->{level}] $label ($percent)\n" ;
263     #open(FP, sprintf(">/tmp/img.%.3i.png", $debug)) ;
264     #print FP $gd->png;
265     #close(FP) ;
266     
267     $debug++ ;
268
269     return $ret ;
270 }
271
272 # on dessine le restant < min_percent
273 sub finalize
274 {
275     my ($self) = @_ ;
276
277     $self->add_part($self->{min_total}, 
278                     "other < $self->{min_percent}%",
279                     $black) ; 
280
281 }
282
283 sub set_title
284 {
285     my ($self, $title) = @_ ;
286
287     $gd->string(GD::gdSmallFont, $self->{center_x} - $self->{rayon}*0.7, 
288                 $self->{center_y}, $title, $black) ;
289 }
290
291 my $_image_map = '';
292
293 sub get_imagemap
294 {
295     my ($self, $title, $img) = @_ ;
296
297     return "
298 <map name='testmap'>
299     $_image_map
300 </map>
301 <img src='$img' border=0 usemap='#testmap' alt=''>
302 " ; 
303
304 }
305
306 sub push_image_map
307 {
308     my ($self, $label, $tips, $start_degrees, $end_degrees) = @_ ;
309
310     if ($label =~ /^other .*</) {
311         if (!$self->{display_other}) {
312             return ;
313         }
314         $label = '';
315     }
316
317     # on prend des points tous les $delta sur l'arc interieur et exterieur
318
319     my $delta = 3 ;
320
321     if (($end_degrees - $start_degrees) < $delta) {
322         return ;
323     }
324
325     my @pts ;
326
327     for (my $i = $start_degrees ; 
328          $i <= $end_degrees ; 
329          $i = $i + $delta)
330     {
331         my ($x1, $y1) = $self->calc_xy($self->{level}, $i) ;
332         my ($x2, $y2) = $self->calc_xy($self->{level} + 1, $i) ;
333
334         push @pts, sprintf("%.2f,%.2f",$x1,$y1) ;
335         unshift @pts, sprintf("%.2f,%.2f",$x2, $y2) ;
336     }
337
338     my ($x1, $y1) = $self->calc_xy($self->{level}, $end_degrees) ;
339     my ($x2, $y2) = $self->calc_xy($self->{level} + 1, $end_degrees) ;
340
341     push @pts, sprintf("%.2f,%.2f",$x1,$y1) ;
342     unshift @pts, sprintf("%.2f,%.2f",$x2, $y2) ;
343
344     my $ret = join(",", @pts) ;
345
346     # on refait le traitement avec $i = $end_degrees
347     $_image_map .= "<area shape='polygon' coords='$ret' ". 
348                    "title='$tips' href='$self->{base_url}$label'>\n" ;
349
350 }
351
352 sub get_labels_imagemap
353 {
354     my ($self) = @_ ;
355     my @ret ;
356
357     for my $l (@draw_label)
358     {
359         # translation 
360         my ($label, $x, $y, $level) = @{ $l } ;
361         
362         next if ($level > $self->{max_label_level}) ;
363
364         next if (!$self->{display_other} and $label =~ /^other .*</) ;
365
366         my $dy = ($y - $self->{center_y})*($last_level - $level) + $y ;
367
368         my $x2 ;
369         my $xp ;
370
371         if ($x < $self->{center_x}) {
372             $x2 =   $self->{center_x} 
373                   - $self->{rayon} * ($last_level + 3.7) ;
374             $xp = $x2 - (length($label) *6 + 2) ; # moins la taille de la police 
375         } else {
376             $x2 = $self->{center_x} + $self->{rayon} * ($last_level + 3.7) ;
377             $xp = $x2 + 10 ;
378         }
379
380         push @ret, $xp - 1 . ";" . $dy - 6 . ";" . $xp + length($label) * $font_size . ";" . $dy + 10 . "\n" ;
381
382         $gd->rectangle($xp - 1, $dy - 6,
383                        $xp + length($label) * $font_size, 
384                        $dy + 10, $black) ;
385     }
386 }
387
388 my $_label_hauteur ;
389 my $_label_max ;
390 my $_label_pos ;
391 my $_label_init = 0 ;
392
393 # on va stocker les positions dans un bitmap $_label_pos
394
395 # si on match pas la position exacte, on essaye la case
396 # au dessus ou en dessous
397
398 # on a un bitmap pour les labels de gauche et un pour la droite
399 # $_label_pos->[0] et $_label_pos->[1]
400 sub get_label_pos
401 {
402     my ($self, $x, $y) = @_ ;
403
404     unless ($_label_init) {
405         $_label_hauteur   = $self->{rayon} * 2 * $last_level ;
406         # nombre max de label = hauteur max / taille de la font
407         $_label_max =  $_label_hauteur / 12 ;
408         $_label_pos = [ [], [] ] ;
409         $_label_init = 1 ;
410     }
411
412     # on calcule la position du label dans le bitmap
413     use integer ;
414     my $num = $y * $_label_max / $_label_hauteur ;
415     no integer ;
416
417     my $n = 0 ;                 # nombre d'iteration
418     my $l ;
419
420     # on prend le bon bitmap
421     if ($x < $self->{center_x}) {
422         $l = $_label_pos->[0] ;
423     } else {
424         $l = $_label_pos->[1] ;
425     }       
426     
427     # on parcours le bitmap pour trouver la bonne position
428     while (($num - $n) > 0) {
429         if (not $l->[$num]) {
430             last ;
431         } elsif (not $l->[$num + $n]) {
432             $num = $num + $n ;
433             last ;
434         } elsif (not $l->[$num - $n]) {
435             $num = $num - $n ;
436             last ;
437         } 
438         $n++ ;
439     }
440     
441     $l->[$num] = 1 ;            # on prend la position
442     
443     if ($num <= 0) {
444         return 0 ;
445     }
446     
447     # calcul de la position
448     $y = $num * $_label_hauteur / $_label_max ;
449
450     return $y ;
451 }
452
453 sub draw_labels
454 {
455     my ($self) = @_ ;
456
457     $gd->fillToBorder(1, 
458                       1,
459                       $black,
460                       $white) ;
461
462     for my $l (@draw_label)
463     {
464         # translation 
465         my ($label, $x, $y, $level) = @{ $l } ;
466
467         next if ($level > $self->{max_label_level}) ;
468
469         next if (!$self->{display_other} and $label =~ /^other .*</) ;
470
471         my $dx = ($x - $self->{center_x})*($last_level - $level) + $x ;
472         my $dy = ($y - $self->{center_y})*($last_level - $level) + $y ;
473
474         $dy = $self->get_label_pos($dx, $dy) ;
475
476         next unless ($dy) ; # pas d'affichage si pas de place
477         
478         $gd->line( $x, $y,
479                    $dx, $dy,
480                    $black) ;
481
482         my $x2 ;
483         my $xp ;
484
485         if ($x < $self->{center_x}) {
486             $x2 =   $self->{center_x} 
487                   - $self->{rayon} * ($last_level + 3.7) ;
488             $xp = $x2 - (length($label) * $font_size + 2) ; # moins la taille de la police 
489         } else {
490             $x2 = $self->{center_x} + $self->{rayon} * ($last_level + 3.7) ;
491             $xp = $x2 + 10 ;
492         }
493
494         $gd->line($dx, $dy,
495                   $x2, $dy,
496                   $black) ;
497
498         $gd->string(GD::gdSmallFont, $xp, $dy - 5, $label, $black) ;
499     }
500 }
501
502 1;
503 __END__
504
505 package main ;
506
507 my $top = new CCircle() ;
508
509 my $chld1 = $top->add_part(50, 'test') ;
510 my $chld2 = $top->add_part(20, 'test') ;
511 my $chld3 = $top->add_part(10, 'test') ;
512 my $chld4 = $top->add_part(20, 'test') ;
513
514
515 $chld1->add_part(20, 'test1') ;
516 $chld1->add_part(20, 'test1') ;
517
518 $chld2->add_part(20, 'test1') ;
519 $chld2->add_part(20, 'test1') ;
520
521 $chld3->add_part(20, 'test1') ;
522 my $chld5 = $chld3->add_part(20, 'test1') ;
523
524 $chld5->add_part(50, 'test3') ;
525
526 $top->finalize() ;
527 $chld1->finalize() ;
528 $chld2->finalize() ;
529 $chld3->finalize() ;
530 $chld4->finalize() ;
531 $chld5->finalize() ;
532
533 $top->draw_labels() ;
534 # make sure we are writing to a binary stream
535 binmode STDOUT;
536
537 # Convert the image to PNG and print it on standard output
538 print $CCircle::gd->png;