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