1# scaling.tcl -- 2# Make a nice scale for the axes in the Plotchart package 3# 4 5namespace eval ::Plotchart { 6 namespace export determineScale 7 8 # 9 # Try and load the math::fuzzy package for better 10 # comparisons 11 # 12 if { [catch { 13 package require math::fuzzy 14 namespace import ::math::fuzzy::tlt 15 namespace import ::math::fuzzy::tgt 16 }] } { 17 proc tlt {a b} { 18 expr {$a < $b } 19 } 20 proc tgt {a b} { 21 expr {$a > $b } 22 } 23 } 24} 25 26# determineScaleFromList -- 27# Determine nice values for an axis from a list of values 28# 29# Arguments: 30# values List of values 31# inverted Whether to return values for an inverted axis (1) or not (0) 32# Defaults to 0. 33# Result: 34# A list of three values, a nice minimum and maximum 35# and stepsize 36# Note: 37# Missing values (empty strings) are allowed in the list of values 38# 39proc ::Plotchart::determineScaleFromList { values {inverted 0} } { 40 41 set xmin {} 42 set xmax {} 43 44 foreach v $values { 45 if { $v == {} } { 46 continue 47 } 48 if { $xmin == {} || $xmin > $v } { 49 set xmin $v 50 } 51 if { $xmax == {} || $xmax < $v } { 52 set xmax $v 53 } 54 } 55 56 return [determineScale $xmin $xmax $inverted] 57} 58 59# determineScale -- 60# Determine nice values for an axis from the given extremes 61# 62# Arguments: 63# xmin Minimum value 64# xmax Maximum value 65# inverted Whether to return values for an inverted axis (1) or not (0) 66# Defaults to 0. 67# Result: 68# A list of three values, a nice minimum and maximum 69# and stepsize 70# Note: 71# xmin is assumed to be smaller or equal xmax 72# 73proc ::Plotchart::determineScale { xmin xmax {inverted 0} } { 74 set dx [expr {abs($xmax-$xmin)}] 75 76 if { $dx == 0.0 } { 77 if { $xmin == 0.0 } { 78 return [list -0.1 0.1 0.1] 79 } else { 80 set dx [expr {0.2*abs($xmax)}] 81 set xmin [expr {$xmin-0.5*$dx}] 82 set xmax [expr {$xmin+0.5*$dx}] 83 } 84 } 85 86 # 87 # Very small ranges (relatively speaking) cause problems 88 # The range must be at least 1.0e-8 89 # 90 if { $dx < 0.5e-8*(abs($xmin)+abs($xmax)) } { 91 set xmean [expr {0.5*($xmin+$xmax)}] 92 set dx [expr {1.0e-8*$xmean}] 93 set xmin [expr {$xmean - 0.5*$dx}] 94 set xmax [expr {$xmean + 0.5*$dx}] 95 } 96 97 # 98 # Determine the factor of 10 so that dx falls within the range 1-10 99 # 100 set expon [expr {int(log10($dx))}] 101 set factor [expr {pow(10.0,$expon)}] 102 103 set dx [expr {$dx/$factor}] 104 105 foreach {limit step} {1.4 0.2 2.0 0.5 5.0 1.0 10.0 2.0} { 106 if { $dx < $limit } { 107 break 108 } 109 } 110 111 set fmin [expr {$xmin/$factor/$step}] 112 set fmax [expr {$xmax/$factor/$step}] 113# if { abs($fmin) > 1.0e10 } { 114# set fmin [expr {$fmin > 0.0 ? 1.0e10 : -1.0e10}] 115# } 116# if { abs($fmax) > 1.0e10 } { 117# set fmax [expr {$fmax > 0.0 ? 1.0e10 : -1.0e10}] 118# } 119 set nicemin [expr {$step*$factor*wide($fmin)}] 120 set nicemax [expr {$step*$factor*wide($fmax)}] 121 122 if { [tlt $nicemax $xmax] } { 123 set nicemax [expr {$nicemax+$step*$factor}] 124 } 125 if { [tgt $nicemin $xmin] } { 126 set nicemin [expr {$nicemin-$step*$factor}] 127 } 128 129 if { !$inverted } { 130 return [list $nicemin $nicemax [expr {$step*$factor}]] 131 } else { 132 return [list $nicemax $nicemin [expr {-$step*$factor}]] 133 } 134} 135 136# determineTimeScale -- 137# Determine nice date/time values for an axis from the given extremes 138# 139# Arguments: 140# tmin Minimum date/time 141# tmax Maximum date/time 142# Result: 143# A list of three values, a nice minimum and maximum 144# and stepsize 145# Note: 146# tmin is assumed to be smaller or equal tmax 147# 148proc ::Plotchart::determineTimeScale { tmin tmax } { 149 set ttmin [clock scan $tmin] 150 set ttmax [clock scan $tmax] 151 152 set dt [expr {abs($ttmax-$ttmin)}] 153 154 if { $dt == 0.0 } { 155 set dt 86400.0 156 set ttmin [expr {$ttmin-$dt}] 157 set ttmax [expr {$ttmin+$dt}] 158 } 159 160 foreach {limit step} {2.0 0.5 5.0 1.0 10.0 2.0 50.0 7.0 300.0 30.0 1.0e10 365.0} { 161 if { $dt/86400.0 < $limit } { 162 break 163 } 164 } 165 166 set nicemin [expr {$step*floor($ttmin/$step)}] 167 set nicemax [expr {$step*floor($ttmax/$step)}] 168 169 if { $nicemax < $ttmax } { 170 set nicemax [expr {$nicemax+$step}] 171 } 172 if { $nicemin > $ttmin } { 173 set nicemin [expr {$nicemin-$step}] 174 } 175 176 set nicemin [expr {int($nicemin)}] 177 set nicemax [expr {int($nicemax)}] 178 179 return [list [clock format $nicemin -format "%Y-%m-%d %H:%M:%S"] \ 180 [clock format $nicemax -format "%Y-%m-%d %H:%M:%S"] \ 181 $step] 182} 183 184if 0 { 185 # 186 # Some simple test cases 187 # 188 namespace import ::Plotchart::determineScale 189 puts [determineScale 0.1 1.0] 190 puts [determineScale 0.001 0.01] 191 puts [determineScale -0.2 0.9] 192 puts [determineScale -0.25 0.85] 193 puts [determineScale -0.25 0.7999] 194 puts [determineScale 10001 10010] 195 puts [determineScale 10001 10015] 196} 197if 0 { 198 puts [::Plotchart::determineTimeScale "2007-01-15" "2007-01-16"] 199 puts [::Plotchart::determineTimeScale "2007-03-15" "2007-06-16"] 200} 201