On Tue, Apr 18, 2006 at 09:58:20PM +0200, Bill Allombert wrote: > There are some ticks above the graph now:
Oh dear. See below. > > The default for max_y_ticks is the same in both cases, 100. The > > 'integer_ticks_only' code aims for a tick interval of 1, but if that goes > > over max_y_ticks, it starts compensating. Without 'integer_ticks_only', > > the 'max_y_ticks' kicks in only with non-integer tick intervals. > > Ah, so this is the intended behaviour ? Weird. The original upstream behaviour with 'integer_ticks_only' is to always use a tick interval of 1 (or whatever is in 'skip_int_ticks'), and not to care about eg. 'max_y_ticks' at all. This is handled with separate logic from the non-integer case. I have been patching this separate logic to take 'max_y_ticks' etc. into account. But you're right, this is not the optimal solution. The attached patch is a new approach: it uses the same code in both integer and non-integer cases. The biggest special case left is now when a fixed tick interval is explicitly given via 'skip_int_ticks'. I think this is more robust than the previous solutions. I have tried to test it more carefully this time, as my track record on buggy patches on this issue is getting embarrassing. Sorry about that, and thanks for your patience. I would appreciate once again if you could test this one too. Cheers, -- Niko Tyni [EMAIL PROTECTED]
--- Chart/Base.pm.orig 2006-04-09 14:47:02.000000000 +0300 +++ Chart/Base.pm 2006-04-19 14:20:27.136041124 +0300 @@ -41,6 +41,7 @@ use strict; use Carp; use FileHandle; +use POSIX qw(floor ceil); $Chart::Base::VERSION = '2.4.1'; @@ -651,9 +652,6 @@ # default value for skip_y_ticks for the labels $self->{skip_y_ticks} = 1; - # default value for skip_int_ticks only for integer_ticks_only - $self->{skip_int_ticks} = 1; - # default value for precision $self->{precision} = 3; @@ -1845,6 +1843,7 @@ my @tickLabels; # List of labels for each tick. my $maxtickLabelLen = 0; # The length of the longest tick label. my $prec_test=0; # Boolean which indicate if precision < |rangeExponent| + my $precision = $self->{precision}; my $temp_rangeExponent; # Find the datatset minimum and maximum. @@ -1866,7 +1865,24 @@ } } } - + # Allow the dataset range to be overidden by the user. + # f_min/max are booleans which indicate that the min & max should not be modified. + my $f_min = defined $self->{'min_val'}; + $d_min = $self->{'min_val'} if $f_min; + + my $f_max = defined $self->{'max_val'}; + $d_max = $self->{'max_val'} if $f_max; + + # Assert against the min is larger than the max. + if( $d_min > $d_max ) + { + croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; + } + # Calculate the width of the dataset. (possibly modified by the user) + my $d_width = $d_max - $d_min; + + my $f_interval = 0; # Forced tick interval from 'skip_int_ticks' + if ( $self->{'integer_ticks_only'} =~ /^\d$/ ) { if ( $self->{'integer_ticks_only'} == 1 ) @@ -1880,87 +1896,53 @@ } if( $self->{'integer_ticks_only'} =~ m!^true$!i ) { - # Allow the dataset range to be overidden by the user. - # f_min/max are booleans which indicate that the min & max should not be modified. - my $f_min = defined $self->{'min_val'}; - $d_min = $self->{'min_val'} if $f_min; - - my $f_max = defined $self->{'max_val'}; - $d_max = $self->{'max_val'} if $f_max; - - # Assert against the min is larger than the max. - if( $d_min > $d_max ) - { - croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; + $precision = 0; + $f_interval = $self->{'skip_int_ticks'}; + if ($f_interval && $f_interval =~ /\D/) { + croak "The specified 'skip_int_ticks' ($f_interval) must be a positive integer"; } - # The user asked for integer ticks, force the limits to integers. - # & work out the range directly. - #$p_min = $self->_round2Tick($d_min, 1, -1); - #$p_max = $self->_round2Tick($d_max, 1, 1); - - $skip = $self->{skip_int_ticks}; - $skip = 1 if $skip < 1; - - $p_min = $self->_round2Tick($d_min, 1, -1); - $p_max = $self->_round2Tick($d_max, 1, 1); - if ( ($p_max - $p_min ) == 0 ) - { - $p_max++, $p_min--; + + # Round the bounds regardless of 'f_min' or 'f_max' + $d_min = floor($d_min); + $d_max = ceil($d_max); + + $d_width = $d_max - $d_min; + + my $interval = 1; + if ($f_interval) { + $interval = $f_interval; } - $tickInterval = $skip; - $tickCount = ($p_max - $p_min ) / $skip + 1; - + # See if there's enough room for 'min_y_ticks' + # If not, expand the range + if ($d_width < ($self->{'min_y_ticks'} - 1) * $interval) { + if (!$f_max || $f_min) { + $d_max = $d_min + ($self->{'min_y_ticks'} - 1) * $interval; + $f_max = 0 if $f_min; + } else { + $d_min = $d_max - ($self->{'min_y_ticks'} - 1) * $interval; + } + $d_width = $d_max - $d_min; + } - # Now sort out an array of tick labels. - - for( my $labelNum = $p_min; $labelNum<$p_max+$tickInterval/3; $labelNum+=$tickInterval ) - { - my $labelText; - - if ( defined $self->{f_y_tick} ) - { - # Is _default_f_tick function used? - if ( $self->{f_y_tick} == \&_default_f_tick) - { - $labelText = sprintf("%d", $labelNum); - } - else - { - $labelText = $self->{f_y_tick}->($labelNum); - } - } - else - { - $labelText = sprintf("%d", $labelNum); - } - - push @tickLabels, $labelText; - $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; + # See if a forced tick interval would mean too many ticks + if ($f_interval && $d_width > ($self->{'max_y_ticks'} - 1) * $f_interval) { + $f_interval = 0; } } - else - { - # Allow the dataset range to be overidden by the user. - # f_min/max are booleans which indicate that the min & max should not be modified. - my $f_min = defined $self->{'min_val'}; - $d_min = $self->{'min_val'} if $f_min; - - my $f_max = defined $self->{'max_val'}; - $d_max = $self->{'max_val'} if $f_max; - - # print "fmin $f_min fmax $f_max\n"; - # print "dmin $d_min dmax $d_max\n"; - - # Assert against the min is larger than the max. - if( $d_min > $d_max ) - { - croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; + + if ($f_interval) { + # Special case for tick calculation: the tick interval is forced + $tickInterval = $f_interval; + $p_min = $d_min; + $p_max = $d_max; + if (!$f_min) { + $p_min -= ($p_min % $f_interval); } + $tickCount = ($p_max - $p_min) / $f_interval + 1; + } else { + # Normal case: calculate the tick interval from the range - # Calculate the width of the dataset. (possibly modified by the user) - my $d_width = $d_max - $d_min; - # If the width of the range is zero, forcibly widen it # (to avoid division by zero errors elsewhere in the code). if ( $d_width == 0 ) @@ -2002,13 +1984,12 @@ # print "pmin $p_min pmax $p_max\n"; # print "range exponent $rangeExponent\n"; - #get the precision for the labels - my $precision = $self->{'precision'}; - if ( $temp_rangeExponent != 0 && $rangeExponent < 0 && $temp_rangeExponent > $precision) { $prec_test = 1; } + + } # !$f_interval # Now sort out an array of tick labels. for( my $labelNum = $p_min; $labelNum<$p_max+$tickInterval/2; $labelNum+=$tickInterval ) @@ -2043,7 +2024,6 @@ push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } # end for - } # Store the calculated data. #### begin debugging output --- Chart/HorizontalBars.pm.orig 2006-02-16 17:54:20.000000000 +0200 +++ Chart/HorizontalBars.pm 2006-04-19 12:02:20.172207627 +0300 @@ -377,7 +377,7 @@ $p_min = $self->_round2Tick($d_min, 1, -1); $p_max = $self->_round2Tick($d_max, 1, 1); - my $skip = $self->{skip_int_ticks}; + my $skip = $self->{skip_int_ticks} || 1; $tickInterval = $skip; $tickCount = ($p_max - $p_min ) /$skip +1;