## ## The cuttlefish Visualization Tool. ## Copyright (C) 2006 The Regents of the University of California. ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License version 2 as published ## by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## written by Bradley Huffaker ## ## documention by Joshua Polterock ## Bradley Huffaker ## Marina Fomenkova ## ## # Legend # This class is used to display the Color Key which shows the # relationship between the location's value and bar height and color. # package Legend; use GD; use Canvas; use Time::Local; use Carp; use Map; use Util; require Exporter; @ISA = qw( Exporter ); @EXPORT = qw(); @EXPORT_OK = qw(); use strict; my $default_font = gdSmallFont; sub new { my ($this) = @_; my $colorKey = bless { "type" => "Legend", "x" => 0, "y" => 0, "width" => undef, "height" => 30, "title" => "Per location:", "font" => $default_font, "y-axis" => "", "min" => undef, "max" => undef, "node-size" => undef, "background-color" => [0,0,0], "foreground-color" => [255,255,255], "tick-color" => [100,100,100], "frames" => [] }, $this; return $colorKey; } sub check { my ($this) = @_; my @missing; foreach my $key (qw(x y width height)) { unless (defined $this->{$key}) { push @missing, $key; } } if ($#missing > 0) { return "undefined ",join(",", @missing); } return; } sub setValue { my ($this, $key, $value) = @_; if ($key eq "x-axis" || $key eq "y-axis" || $key eq "title") { $this->{$key} = $value; } elsif ($key =~ /font/) { return $this->setFont($key,$value); } elsif ($key =~ /-color$/) { return $this->setColor($key, $value); } elsif ($key eq "width") { return "Legend:setValue() width can not be set, product of node-size"; } elsif ($key eq "node-size-scale" || $key eq "node-type") { $this->{$key} = $value; } elsif ($key eq "node-size") { my $error = $this->setNumberValue("setValue", $key, $value); return $error if (defined $error); return $this->setWidth(); } else { return $this->setNumberValue("setValue", $key, $value); } return; } sub setFont { my ($this, $key, $value) = @_; my $font = String2Font($value); unless (defined $font) { return "Legend::setValue $font is not a valid font"; } $this->{$key} = $font; return; } sub setColor { my ($this, $key, $value) = @_; my @values = split /\s+/, $value; my $error_msg; my $error_found; foreach my $v (0..2) { if ($v =~ /^\d+$/) { $error_msg .= "$v " } else { $error_msg .= "\"$v\" "; $error_found = 1; } } if (defined $error_found) { return "Legend::setValue requires three integers, found $error_msg"; } $this->{$key} = \@values; return; } sub setNumberValue { my ($this, $func, $key, $value) = @_; unless (defined $value && $value =~ /^-?\d+(\.\d+)?$/) { return "Legend::$func\($key\) \"$value\" is not a number"; } $this->{$key} = $value; return; } sub setValueMinMax { my ($this, $value_min, $value_max) = @_; $this->{"min"} = $value_min; $this->{"max"} = $value_max; } sub setWidth($) { my ($this) = @_; my $font = $this->{font}; my $bar_height = $this->{"node-size"}; my $width = $bar_height; $width += $font->height + (8+3+3+3+9)*$font->width; $this->{width} = $width; } ############################################################################ # Check Images and Values ########################################################################### sub draw { my ($this, $canvas, $index) = @_; my $x = $this->{x}; my $y = $this->{y}; my $min = $this->{"min"}; my $max = $this->{"max"}; $canvas->CheckBins($min,$max); my $width = $this->drawHeightKey($canvas, $x,$y); $this->drawLegend($canvas, $x+$width, $y); #my $width = $this->{width}; #my $height = $this->{height}; #my $color = $canvas->getColor(255,255,255); #$canvas->{IMAGE}->rectangle($x, $y, $x+$width, $y+$height, $color); } sub getMap { my ($this) = @_; my $map = new Map(); foreach my $key (qw(node-size node-size-scale node-type)) { $map->setValue($key, $this->{$key}); } return $map; } sub drawLegend { my ($this, $canvas, $graph_x, $graph_y) = @_; my $image = $canvas->{IMAGE}; my $foreground = $canvas->getColor(@{$this->{"foreground-color"}}); my $font = $this->{font}; my $min = $this->{"min"}; my $max = $this->{"max"}; my $map = $this->getMap(); my $width_bar = $map->nodeSize($max,$min,$max); my $width = 9*$font->width + $width_bar; my $height = $this->{height}; my $title = $this->{title}; my $title_y = $graph_y; if ($title =~ /[^\s]/) { my $size = 1.5*$font->height; $graph_y += $size; $height -= $size; } my @bins = @{$canvas->{bins}}; my %label_width; foreach my $type ("start", "end") { foreach my $i (0..$#bins) { my $label = CompressNumber($bins[$i]{$type}); my $length = length($label); my $current = $label_width{$type}; if (!defined $current || $current < $length) { $label_width{$type} = $length; } } } $height -= $font->height; foreach my $i (0..$#bins) { my $x = $graph_x; my $y = $graph_y; if ($#bins > 0) { $y += $height*(($#bins-$i)/$#bins); } my $start = CompressNumber($bins[$i]{"start"}); my $end = CompressNumber($bins[$i]{"end"}); my $color = $bins[$i]{"color"}; my $start_width = $label_width{start}; my $end_width = $label_width{end}; my $start_x = $x + ($start_width-length($start))*$font->width; $x += $start_width*$font->width; my $dash_x = $x; $x += $font->width; my $end_x = $x + ($end_width-length($end))*$font->width; $x += $end_width*$font->width + 2; $image->string($font, $start_x, $y, $start,$foreground); if ($end != $start) { $image->string($font, $dash_x, $y, "-",$foreground); $image->string($font, $end_x, $y, $end,$foreground); } my $x0 = $x; my $y0 = $y; my $x1 = $x0 + $font->width*4; my $y1 = $y0 + $font->height; $image->filledRectangle($x0, $y0, $x1, $y1, $color); } } sub drawHeightKey { my ($this, $canvas, $start_x, $start_y) = @_; my $graph_x = $start_x; my $graph_y = $start_y; my $image = $canvas->{IMAGE}; my $foreground = $canvas->getColor(@{$this->{"foreground-color"}}); my $tick_color = $canvas->getColor(@{$this->{"tick-color"}}); my $font = $this->{font}; my $min = $this->{"min"}; my $max = $this->{"max"}; my $map = $this->getMap(); my $width = 4*$font->width + $font->height + $map->nodeSize($max,$min,$max); my $height = $this->{height}; my $title = $this->{title}; my $y_axis = $this->{"y-axis"}; $height -= $font->height; ####################################################### # shift for title, axis ####################################################### my $title_y = $graph_y; if ($title =~ /[^\s]/) { my $size = 1.5*$font->height; $graph_y += $size; $height -= $size; } my $y_axis_x = $graph_x; if ($y_axis =~ /[^\s]/) { $width -= $font->height; $graph_x += $font->height; } my $title_x = $graph_x; ####################################################### # shift for ticks ####################################################### my ($sum_len, @y_key_values) = $this->CreateSumKeys($height); $graph_x += $sum_len; ####################################################### # Draw data ####################################################### my $deactive = $canvas->getColor(150,150,255); my $active = $canvas->getColor(200,200,255); my $range = $max-$min; foreach my $i (1..$height) { my $fraction = $i/$height; my $x0= $graph_x; #my $value = $val_diff*(log($i)/log($height))+$min; #my $value = $this->index2val($val_diff, $min, $i,$height); my $value = $range*$fraction + $min; my $y0 = $graph_y + $height - $i; my $size = $map->nodeSize($value,$min,$max); #print "value:$value $size\n"; my $x1 = $graph_x + $size; my $y1 = $y0; my $color = $canvas->getColorMinMax($value, $min, $max); $image->line($x0, $y0, $x1, $y1, $color); } ####################################################### # Draw title, axis ####################################################### foreach my $i (0..$#y_key_values) { my ($key, $val) = @{$y_key_values[$i]}; my $x = $graph_x - $font->width*length($key); my $h = $this->val2index($max, $min, $val, $height)+1; my $y = $graph_y + $height - $h; $image->string($font, $x, $y-$font->height/2, $key, $foreground); if ($i != 0) { my $x = $graph_x; my $xx = $x + $font->width; $image->line($x, $y, $xx, $y, $tick_color); } } ####################################################### # Draw title, axis ####################################################### if ($title =~ /[^\s]/) { my $x = $title_x;# + ($width - $font->width*length($title))/2; my $y = $title_y; $image->string($font, $x, $y, $title,$foreground); } if ($y_axis =~ /[^\s]/) { my $x = $y_axis_x; my $y = $graph_y + ($height + $font->width*length($y_axis))/2; $y = int($y); $image->stringUp($font, $x, $y, $y_axis,$foreground); } return ($graph_x + $map->nodeSize($max,$min,$max)+$font->width) - $start_x; } sub CreateSumKeys { my ($this, $height) = @_; my $max = $this->{"max"}; my $min = $this->{"min"}; my $font = $this->{font}; my $hash = { "min" => 0, "max" => $max, "width" => $height, "font_size" => $font->height, "compress" => 1 }; my ($unit, $label_length, @key_values) = Range2Units($hash); return ($label_length, @key_values); } sub index2val { my ($this, $max, $min, $i, $height) = @_; return $max*($i/$height) + $min; } sub val2index { my ($this, $max, $min, $val, $height) = @_; $max = 0.00000001 if ($max == 0); return $height*(($val-$min)/$max); }