Image-BoxModel

 view release on metacpan or  search on metacpan

lib/Image/BoxModel/Chart.pm  view on Meta::CPAN

				color 	=> $p{grid_color}
			);
		}
	}
	else{
		#unimplemented
		print "horizontal grid unimplemented. sorry.";
	}
}

sub DrawTicks{
	my $image = shift;
	my %p = @_;
	
	if ($p{orientation} =~ /vertical/){	
		foreach (@{$p{array}}){
			
			my $y = where_between (
				pos_min => $image->{$p{box_to_measure_from}}{bottom} - $p{box_border},
				pos_max => $image->{$p{box_to_measure_from}}{top} 	 + $p{box_border},
				val_min => $p{lowest},
				val_max => $p{highest},
				val		=> $_
			);
			
			$image -> DrawRectangle (
				top 	=> $y - ($p{thickness}-1)/2, # see above, DrawGrid
				bottom 	=> $y + ($p{thickness}-1)/2, 
				right 	=> $image -> {$p{box_to_draw_on}}{right},
				left 	=> $image -> {$p{box_to_draw_on}}{left},
				color 	=> 'black' # to be dony by parameter
			);
		}
	}
	else{
		for my $c (0 .. scalar(@{$p{array}})-1){
			my $x = where_between (
				pos_min => $image->{$p{box_to_measure_from}}{left}  + $p{box_border},
				pos_max => $image->{$p{box_to_measure_from}}{right} - $p{box_border},
				val_min => $p{lowest},
				val_max => $p{highest},
				val		=> $c +.5 # middle of the bar / point / whatever is in the middle between 2 borders..
			);
			
			$image -> DrawRectangle (
				left 	=> $x-($p{thickness}-1)/2, # see above, DrawGrid
				right 	=> $x+ ($p{thickness}-1)/2, 
				bottom 	=> $image -> {$p{box_to_draw_on}}{bottom},
				top 	=> $image -> {$p{box_to_draw_on}}{top},
				color 	=> 'black' # to be dony by parameter
			);
		}
	}
}

=head3 Legend

 $image -> Legend(
	#mandatory:
	font 			=> (path to font file),
	name 			=> (name of box in which the legend lives)
	values_annotations => (name of your datasets)
	
	#optional (dafaults preset):
	textsize 		=> [number],
	rotate 			=> [number], 
	colors 			=> (color names of datasets), 	#nice: 'colors => DefaultColors()' sets default colors
	position 		=> ['right'|'left],
	orientation 	=> 'vertical',					#horizontal is unimplemented so far
	resize 			=> (name of box to be resized),
	background 		=> (color),
	
	padding_left 	=> [number],
	padding_right 	=> [number],
	padding_top 	=> [number],
	padding_bottom 	=> [number],
	
	spacing_left 	=> [number],
	spacing_top 	=> [number],
	spacing_right 	=> [number],
	spacing_bottom 	=> [number],
	
	border 			=> [number],
	border_color 	=> (color),
 );

Draw Legend. 

=cut

sub Legend{
	my $image = shift;
	my %p = (
		textsize 		=> 12,
		rotate 			=> 0, 
		colors 			=> DefaultColors(),
		position 		=> 'right',
		orientation 	=> 'vertical',
		resize 			=> 'free',
		background 		=> $image->{background},
		
		padding_left 	=> 10,
		padding_right 	=> 10,
		padding_top 	=> 10,
		padding_bottom 	=> 10,
		
		spacing_left 	=> 10,
		spacing_top 	=> 10,
		spacing_right 	=> 10,
		spacing_bottom 	=> 10,
		
		border 			=> 1,
		border_color	=> 'black',
		
		@_
	);
	
	if (exists $p{values_annotations} and $p{values_annotations}){
		$p{values_ref} = $p{values_annotations};
	}
	else{
		croak __PACKAGE__, ": Mandatory parameter 'values_annotations' missing";
	}
	
	croak __PACKAGE__, ": Mandatory parameter 'name' missing" unless (exists $p{name} and $p{name});
	
	my $square_size = int ($p{textsize} * .8);	#to be done by some intelligently set parameters later on..

	my ($w, $h) = $image -> ArrayBox (resize => $p{name},
		name 		=> "$p{name}_text",
		background 	=> $p{background},
		position 	=> $p{position},
		orientation => $p{orientation},
		values_ref 	=> $p{values_ref},
		textsize 	=> $p{textsize},
		rotate 		=> $p{rotate},
		font 		=> $p{font},
		no_box 		=> 1
	);
	
	#~ print "Width: $w, height: $h\n";
	
	#idea: have a big box into which the smaller boxes for legend etc go.
	
	$image -> Box (
		name 		=> "$p{name}",
		width 		=> $p{padding_left} + $p{border} + $p{spacing_left} + $square_size + $p{spacing_left} + $w + $p{spacing_right} + $p{border} + $p{padding_right}+6,
		height 		=> $p{padding_top} + $p{border} + $p{spacing_top} + $h + $p{spacing_bottom} + $p{border} + $p{padding_bottom}+4,
		position 	=> $p{position},
		resize 		=> $p{resize},
	);
	
	#~ print "Top: $image->{$p{name}}{top}, bottom: $image->{$p{name}}{bottom}\n";
	
	foreach ('left', 'right', 'top', 'bottom'){	#padding: 4 little (big) boxes outside the border, one at each corner
		$image -> Box (
			resize 	=> "$p{name}",
			width 	=> $p{"padding_$_"},
			height 	=> $p{"padding_$_"},
			name 	=> "$p{name}_padding_$_",
			position=> "$_",
		);
	}
	
	foreach ('left', 'right', 'top', 'bottom'){	#spacing: 4 little (big) boxes inside the border, one at each corner
		$image -> Box (
			resize 	=> "$p{name}",
			width 	=> $p{"spacing_$_"} + $p{border},	#to reserve space for the border as well..
			height 	=> $p{"spacing_$_"} + $p{border},
			name 	=> "$p{name}_spacing_$_",
			position=> "$_",
		);
	}
	
	$image -> ArrayBox (		#reserve space for the text
		resize 		=> $p{name},
		name 		=> "$p{name}_text",
		background 	=> $p{background},
		position 	=> 'right',	# Text is *always" right of little squares, wherever the legend is put.
		orientation => $p{orientation},
		values_ref 	=> $p{values_ref},
		textsize 	=> $p{textsize},
		rotate 		=> $p{rotate},
		font 		=> $p{font},
	);
	
	$image -> Box(			#some spacing between text & squares
		resize 		=> $p{name},
		name		=> "$p{name}_spacing_text_squares",
		width 		=> $p{spacing_left},
		position 	=> 'right'
	);
	
	$image -> Box(				#box for squares
		resize 		=> $p{name},
		name		=> "$p{name}_squares",
		width 		=> $square_size,
		height 		=> $square_size,
		position 	=> $p{position},
	);
	
	
	$image -> DrawRectangle(		#a rectangle as border of the legend
		top 		=> $image ->{"$p{name}_spacing_top"}{top},
		bottom 		=> $image->{"$p{name}_spacing_top"}{top}+ $p{border} * 2 + $h+ $p{spacing_top} + $p{spacing_bottom}, # Calculate space needed. 
		left 		=> $image->{"$p{name}_spacing_left"}{left}, 
		right 		=> $image->{"$p{name}_spacing_right"}{right},  
		fill_color 	=> $p{background}, 
		border_color=> $p{border_color},
		border_thickness => $p{border}
	)if ($p{border});
	
	#~ print $image->{"$p{name}_spacing_top"}{top}, "\t", $p{border} * 2 ,"\t", $h, "\t", $p{spacing_top} ,"\t", $p{spacing_bottom},"\n";
	#~ print $image->{"$p{name}_spacing_top"}{top}+ $p{border} * 2 + $h+ $p{spacing_top} + $p{spacing_bottom}, "\n";
	
	
	
	
	#~ print  $image->{"$p{name}_spacing_top"}{top}+ $p{border} * 2 + $p{spacing_top} + $h + $p{spacing_bottom};
	
	foreach (0.. scalar(@{$p{values_ref}})-1){
		#~ #print @{$p{colors}}[$_], "\t", @{$p{values_ref}}[$_], "\n";
		
		my ($width, $height) = $image -> GetTextSize(
			text 		=> @{$p{values_ref}}[$_],
			textsize 	=> $p{textsize},
			rotate	 	=> $p{rotate},
			font 		=> $p{font}
		);
		
		#there will be a distinction between vertically and horizontally drawn legends as soon as this is implemented
		
		my $e = $image -> Annotate(
			resize 		=>"$p{name}_text",
			text 		=> @{$p{values_ref}}[$_], 
			textsize 	=> $p{textsize},
			rotate 		=> $p{rotate},
			align 		=> 'left', 
			text_position=> 'west',
			font 		=> $p{font},
			
		);
	
		
		my $center_of_minibox = ($image->{$e}{top} + $image->{$e}{bottom}) / 2;
		
		$image -> DrawRectangle(
			top 		=> $center_of_minibox - $square_size / 2, 
			bottom 		=> $center_of_minibox + $square_size / 2, 
			#~ #top 	=> $image->{$e}{top},
			#~ #bottom 	=> $image->{$e}{bottom},
			left 		=> $image->{"$p{name}_squares"}{left}, 
			right		=> $image->{"$p{name}_squares"}{right},  
			fill_color 	=> @{$p{colors}}[$_], 
			border_color=> 'black'
		);
	}
}

sub where_between{#calculates where on a picture a value has to be painted between two points
	my %p = @_;
	
	foreach ('pos_min', 'pos_max', 'val_min', 'val_max', 'val'){
		Carp::croak ("where_between: missing parameter $_") unless (exists $p{$_});
	}
	
	my $position =  (
		$p{pos_min} 					#minimum position 
		+
		($p{pos_max} - $p{pos_min})		#distance between max & min
		*
		
		($p{val} - $p{val_min})			#difference between value and minimum value (numbers, not position!)
		/ 
		($p{val_max} - $p{val_min})		#difference between max & minimum
		#3 lines above result in a factor between 0 an 1, 0 if val = min, 1 if val = max, .5 if val in the middle between the both.
		
		#the distance between max & min (which are pixels or whatsoever) are multiplied by the factor (0-1).
		#this way, the distance between min and position are calculated
		
		#if max < min the result of max-min is negative so that a negative number is added to min. and everybody is happy without any if().
	);
	
	return $position;
}


sub DefaultColors{
	my $image = shift;
	return ['red', 'orange', 'yellow', 'LightGreen', 'green', 'blue', 'DarkBlue', 'DarkRed'];
}



( run in 2.227 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )