language-Modula2-0.1: examples/Modula-2_Libraries/andrea-m2/lib/generic/lewertssc.mod
IMPLEMENTATION MODULE LewertsScale;
(* Modula-2 implementation by John Andrea, Oct.8/92 *)
FROM MathLib0 IMPORT sqrt, power;
FROM MoreMath IMPORT Log10;
VAR
n_set :CARDINAL;
nice_set :ARRAY [1..4] OF REAL;
(* ---------------------------------------------------------- *)
PROCEDURE Scale( data_min, data_max :REAL; approx_inc :CARDINAL;
VAR scale_min, scale_max, scale_inc :REAL );
(* Implementation notes:
* 1) should n_set be large enough to include the 10 or not.
* The book is confusing as it is given in C and there is some
* comment about including the 10 for a special purpose.
* 2) there seems to be a problem with the given algorithm in
* computing the scale-max, so do a hack here to fix it
* 3) there seems to be a problem with the given algorithm in
* computing the scale-min, so do a hack here to fix it
*)
VAR
actual_inc :INTEGER;
interval_size :REAL;
set_index :CARDINAL;
power_of_ten :REAL;
nice_num, geo_mean :REAL;
n_low_mul, n_hi_mul :INTEGER;
BEGIN
IF approx_inc < 2 THEN
actual_inc := 2;
ELSE
actual_inc := INTEGER( approx_inc );
END;
IF data_min > data_max THEN
nice_num := data_min;
data_min := data_max;
data_max := nice_num;
ELSE
(* compute the smallest potential interval *)
interval_size := ( data_max - data_min ) / FLOAT( actual_inc );
(* compute the nice number closest to the approximate potential interval *)
FirstNiceNum( interval_size, set_index, power_of_ten, nice_num );
geo_mean := sqrt( nice_set[set_index] * nice_set[set_index+1] );
WHILE geo_mean * power_of_ten < interval_size DO
NextNiceNum( set_index, power_of_ten, nice_num );
geo_mean := sqrt( nice_set[set_index] * nice_set[set_index+1] );
END;
(* produce the scale using the computed nice number *)
CalcExtLabel( data_min, data_max, nice_num, n_low_mul, n_hi_mul );
(* and the scale limits *)
scale_min := FLOAT( n_low_mul ) * nice_num;
scale_max := FLOAT( n_hi_mul ) * nice_num;
actual_inc := n_hi_mul - n_low_mul;
(* heres the hack to fix the scale_max *)
scale_inc := ( scale_max - scale_min ) / FLOAT( actual_inc );
WHILE scale_max < data_max DO
scale_max := scale_max + nice_num;
END;
(* and fix scale_min too *)
WHILE scale_min > data_min DO
scale_min := scale_min - nice_num;
END;
END;
END Scale;
(* ---------------------------------------------------------- *)
PROCEDURE CalcExtLabel( data_min, data_max, nice_num :REAL;
VAR n_low, n_high :INTEGER);
(* calculate an externally labeled scale *)
VAR
check :REAL;
BEGIN
(* calculate the low multiple *)
n_low := TRUNC( data_min / nice_num );
(* double check it *)
(* make sure that we've got the largest possible minimum *)
check := FLOAT( n_low + 1 ) * nice_num;
IF check <= data_min THEN
n_low := n_low + 1;
END;
(* calculate the high multiple *)
n_high := TRUNC( ( data_max / nice_num ) + 0.5 );
(* double check it *)
(* make sure that we've got the smallest possible maximum *)
check := FLOAT( n_high - 1 ) * nice_num;
IF check >= data_max THEN
n_high := n_high - 1;
END;
END CalcExtLabel;
(* ---------------------------------------------------------- *)
PROCEDURE FirstNiceNum( interval_size :REAL;
VAR set_index :CARDINAL;
VAR power_of_ten, nice_num :REAL );
(* calculate an initial value for the nice-number *)
(* Implementation notes: *)
(* 1) can a real be raised to a negative integer power ? *)
VAR
exponent :INTEGER;
BEGIN
(* calculate an initial power of ten *)
exponent := TRUNC( Log10( interval_size ) );
power_of_ten := power( 10.0, FLOAT( exponent ) );
(* double check it *)
IF power_of_ten * 10.0 <= interval_size THEN
power_of_ten := power_of_ten * 10.0;
END;
(* and the index into the set of number is initialy the first number *)
set_index := 1;
(* and return the first nice-number *)
nice_num := power_of_ten;
END FirstNiceNum;
(* ---------------------------------------------------------- *)
PROCEDURE NextNiceNum( VAR set_index :CARDINAL;
VAR power_of_ten, nice_num :REAL );
(* calculate the next value for the nice-number *)
(* Implementation notes: *)
(* 1) should the check for the exceeded set size *)
(* be ".ge." as it is in the book, or ".gt." *)
(* since FORTRAN starts indicies at one rather *)
(* than starting at zero as in the book. *)
BEGIN
(* increment the index *)
set_index := set_index + 1;
(* if the maximum has been passed, then reset index to first *)
(* position, and increase the power of ten *)
IF set_index > n_set THEN
set_index := 1;
power_of_ten := power_of_ten * 10.0;
END;
(* and return the next nice number *)
nice_num := nice_set[set_index] * power_of_ten;
END NextNiceNum;
BEGIN
n_set := 3; (* actual length less one *)
nice_set[1] := 1.0; nice_set[2] := 2.0;
nice_set[3] := 5.0; nice_set[4] := 10.0;
END LewertsScale.