Subject: | Better tick calculation |
I think the automatic tick calculation could be done "better". For example,
if I have data in the interval [0 .. 146], then ticks would be created
at 0, 29.2, 58.4, 87.6, 116.8 and 146, which is ugly. I would expect them
at 0, 25, 50, 75, 100, 125 or 0, 50, 100.
Attached is a sample function make_tics (converted from
gnuplot3.5/gnuplot3.7.3 to perl code) which tries to calculate an
"ideal" distance between two tics. Arguments to make_tics are minimal
and maximal value of the interval and an optional "$guide" argument,
which approximately sets the number of tics. The other two arguments
are only for logarithmic scales and may be neglected.
I would like to see this function or a similar integrated in GD::Graph.
Regards,
Slaven
# von gnuplot3.5 (graphics.c) geklaut
# Modifikationen wie bei 3.7.3
sub make_tics {
my($tmin, $tmax, $guide, $logscale, $base_log) = @_;
$guide = 20 if !defined $guide;
my $xr = abs($tmin - $tmax);
my $l10 = log10($xr);
my $tic = set_tic($l10, $guide);
if ($logscale && $tic < 1) {
$tic = 1;
}
# XXX missing datatype == TIME handling
$tic;
}
sub set_tic {
my($l10, $guide) = @_;
my $fl = floor($l10);
my $xnorm = 10 ** ($l10 - $fl); # approx number of decades
my $posns = $guide / $xnorm; # approx number of tic posns per decade
my $tics;
if ($posns > 40) {
$tics = 0.05; # eg 0, .05, .10, ...
} elsif ($posns > 20) {
$tics = 0.1; # eg 0, .1, .2, ...
} elsif ($posns > 10) {
$tics = 0.2; # eg 0,0.2,0.4,...
} elsif ($posns > 4) {
$tics = 0.5; # 0,0.5,1,
} elsif ($posns > 1) {
$tics = 1; # 0,1,2,....
} elsif ($posns > 0.5) {
$tics = 2; # 0, 2, 4, 6
} else {
# getting desperate... the ceil is to make sure we
# go over rather than under - eg plot [-10:10] x*x
# gives a range of about 99.999 - tics=xnorm gives
# tics at 0, 99.99 and 109.98 - BAD !
# This way, inaccuracy the other way will round
# up (eg 0->100.0001 => tics at 0 and 101
# I think latter is better than former
$tics = ceil($xnorm);
}
$tics * dbl_raise(10.0, $fl);
}
sub dbl_raise {
my($x, $y) = @_;
my $val = 1;
my $i;
for($i = 0; $i < abs($y); $i++) {
$val *= $x;
}
if ($y < 0) {
1/$val;
} else {
$val;
}
}