changeset 1459:3a128665fa6f horizontal-scale

Fixes to logarithmic scale tick intervals. The approach here is not right, though -- and I've left in a failing test or two to remind me of that
author Chris Cannam
date Wed, 02 May 2018 14:17:10 +0100
parents 0fb5d4e6edeb
children 9528c73aa98c
files base/ScaleTickIntervals.h base/test/TestScaleTickIntervals.h
diffstat 2 files changed, 167 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/base/ScaleTickIntervals.h	Tue Apr 24 15:02:54 2018 +0100
+++ b/base/ScaleTickIntervals.h	Wed May 02 14:17:10 2018 +0100
@@ -182,6 +182,11 @@
     {
         Display display = Auto;
 
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+        SVDEBUG << "ScaleTickIntervals::logInstruction: Range is "
+                << r.min << " to " << r.max << endl;
+#endif
+        
         if (r.n < 1) {
             return {};
         }
@@ -196,33 +201,71 @@
 
         double digInc = log10(inc);
         int precInc = int(floor(digInc));
+
         double roundTo = pow(10.0, precInc);
 
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+        SVDEBUG << "ScaleTickIntervals::logInstruction: Naive increment is "
+                << inc << ", of " << digInc << "-digit length" << endl;
+        SVDEBUG << "ScaleTickIntervals::logInstruction: "
+                << "So increment is precision " << precInc
+                << ", yielding rounding for increment of "
+                << roundTo << endl;
+#endif
+        
         if (roundTo != 0.0) {
             inc = round(inc / roundTo) * roundTo;
             if (inc < roundTo) inc = roundTo;
+
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+            SVDEBUG << "ScaleTickIntervals::logInstruction: "
+                    << "Rounded increment to " << inc << endl;
+#endif
         }
 
         // if inc is close to giving us powers of two, nudge it
         if (fabs(inc - 0.301) < 0.01) {
             inc = log10(2.0);
+
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+            SVDEBUG << "ScaleTickIntervals::logInstruction: "
+                    << "Nudged increment to " << inc << " to get powers of two"
+                    << endl;
+#endif
         }
 
         // smallest increment as displayed
         double minDispInc =
             LogRange::unmap(r.min + inc) - LogRange::unmap(r.min);
 
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+        SVDEBUG << "ScaleTickIntervals::logInstruction: "
+                << "Smallest displayed increment is " << minDispInc << endl;
+#endif
+        
         int prec = 1;
 
         if (minDispInc > 0.0) {
-            prec = int(floor(log10(minDispInc)));
+            prec = int(ceil(log10(minDispInc))) - 1;
+            if (prec == 0) prec = 1;
             if (prec < 0) prec = -prec;
+
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+            SVDEBUG << "ScaleTickIntervals::logInstruction: "
+                    << "Precision therefrom is " << prec << endl;
+#endif
         }
 
         if (r.max >= -2.0 && r.max <= 3.0 &&
             r.min >= -3.0 && r.min <= 3.0) {
             display = Fixed;
             if (prec == 0) prec = 1;
+
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+            SVDEBUG << "ScaleTickIntervals::logInstruction: "
+                    << "Min and max within modest range, adjusted precision to "
+                    << prec << " and display to Fixed" << endl;
+#endif
         }
 
 #ifdef DEBUG_SCALE_TICK_INTERVALS
@@ -259,16 +302,58 @@
     }
     
     static Tick makeTick(Display display, int precision, double value) {
+
         if (value == -0.0) {
             value = 0.0;
         }
+        
         const int buflen = 40;
         char buffer[buflen];
-        snprintf(buffer, buflen,
-                 display == Auto ? "%.*g" :
-                 display == Fixed ? "%.*f" :
-                 "%.*e",
-                 precision, value);
+
+        if (display == Auto) {
+
+            int digits = (value != 0.0 ? int(ceil(log10(abs(value)))) : 0);
+
+            // This is not the same logic as %g uses for determining
+            // whether to delegate to use scientific or fixed notation
+
+            if (digits < -3 || digits > 4) {
+
+                display = Auto; // delegate planning to %g
+
+            } else {
+
+                display = Fixed;
+                
+                // in %.*f, the * indicates decimal places, not sig figs
+                if (precision > digits) {
+                    precision -= digits;
+                } else if (precision == digits) {
+                    precision = 1;
+                } else if (precision + 1 < digits) {
+                    double r = pow(10, digits - precision - 1);
+                    value = r * round(value / r);
+                    precision = 0;
+                } else {
+                    precision = 0;
+                }
+            }
+        }
+        
+        const char *spec = (display == Auto ? "%.*g" :
+                            display == Scientific ? "%.*e" :
+                            "%.*f");
+
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+        
+        snprintf(buffer, buflen, spec, precision, value);
+
+#ifdef DEBUG_SCALE_TICK_INTERVALS
+        SVDEBUG << "makeTick: spec = \"" << spec
+                << "\", prec = " << precision << ", value = " << value
+                << ", label = \"" << buffer << "\"" << endl;
+#endif
+        
         return Tick({ value, std::string(buffer) });
     }
     
--- a/base/test/TestScaleTickIntervals.h	Tue Apr 24 15:02:54 2018 +0100
+++ b/base/test/TestScaleTickIntervals.h	Wed May 02 14:17:10 2018 +0100
@@ -428,7 +428,7 @@
         // pathological range
         auto ticks = ScaleTickIntervals::linear({ 1, 1, 10 });
         ScaleTickIntervals::Ticks expected {
-            { 1.0, "1" }
+            { 1.0, "1.0" }
         };
         compareTicks(ticks, expected);
     }
@@ -438,7 +438,7 @@
         // pathological range
         auto ticks = ScaleTickIntervals::linear({ 0, 0, 10 });
         ScaleTickIntervals::Ticks expected {
-            { 0.0, "0" }
+            { 0.0, "0.0" }
         };
         compareTicks(ticks, expected);
     }
@@ -458,7 +458,7 @@
         // senseless input
         auto ticks = ScaleTickIntervals::linear({ 0, 1, 0 });
         ScaleTickIntervals::Ticks expected {
-            { 0.0, "0" },
+            { 0.0, "0.0" },
         };
         compareTicks(ticks, expected);
     }
@@ -468,7 +468,7 @@
         // senseless input
         auto ticks = ScaleTickIntervals::linear({ 0, 1, -1 });
         ScaleTickIntervals::Ticks expected {
-            { 0.0, "0" },
+            { 0.0, "0.0" },
         };
         compareTicks(ticks, expected);
     }
@@ -518,7 +518,7 @@
     {
         auto ticks = ScaleTickIntervals::logarithmic({ M_PI, 6.022140857e23, 7 });
         ScaleTickIntervals::Ticks expected {
-            { 1000, "1e+03" },
+            { 1000, "1000.0" },
             { 1e+06, "1e+06" },
             { 1e+09, "1e+09" },
             { 1e+12, "1e+12" },
@@ -528,7 +528,7 @@
         };
         compareTicks(ticks, expected, true);
     }
-    
+
     void log_0p465_778_10()
     {
         auto ticks = ScaleTickIntervals::logarithmic({ 0.465, 778.08, 10 });
@@ -548,6 +548,76 @@
         compareTicks(ticks, expected);
     }
     
+    void log_1_10k_10()
+    {
+        auto ticks = ScaleTickIntervals::logarithmic({ 1.0, 10000.0, 10 });
+        ScaleTickIntervals::Ticks expected {
+            { 1.0, "1.0" },
+            { 2.5, "2.5" },
+            { 6.0, "6.0" },
+            { 16.0, "16.0" },
+            { 40.0, "40.0" },
+            { 100.0, "100.0" },
+            { 250.0, "250.0" },
+            { 600.0, "600.0" },
+            { 1600.0, "1600.0" },
+            { 4000.0, "4000.0" },
+            { 10000.0, "1e+04" },
+        };
+        compareTicks(ticks, expected, true);
+    }
+    
+    void log_80_10k_6()
+    {
+        auto ticks = ScaleTickIntervals::logarithmic({ 80.0, 10000.0, 6 });
+        ScaleTickIntervals::Ticks expected {
+            { 130, "130" },
+            { 260, "260" },
+            { 510, "510" },
+            { 1000, "1000" },
+            { 2000, "2000" },
+            { 4100, "4100" },
+            { 8200, "8200" }
+        };
+        compareTicks(ticks, expected, true);
+    }
+    
+    void log_80_800k_10()
+    {
+        auto ticks = ScaleTickIntervals::logarithmic({ 80.0, 800000.0, 10 });
+        ScaleTickIntervals::Ticks expected {
+            { 100, "100" },
+            { 251, "251" },
+            { 631, "631" },
+            { 1580, "1580" },
+            { 3980, "3980" },
+            { 10000, "10000" },
+            { 25100, "2.5e+04" },
+            { 63100, "6.3e+04" },
+            { 160000, "1.6e+05" },
+            { 400000, "4e+05" },
+        };
+        compareTicks(ticks, expected, true);
+    }
+    
+    void log_0_1_0()
+    {
+        // senseless input
+        auto ticks = ScaleTickIntervals::logarithmic({ 0, 1, 0 });
+        ScaleTickIntervals::Ticks expected {
+        };
+        compareTicks(ticks, expected);
+    }
+    
+    void log_0_1_m1()
+    {
+        // senseless input
+        auto ticks = ScaleTickIntervals::logarithmic({ 0, 1, -1 });
+        ScaleTickIntervals::Ticks expected {
+        };
+        compareTicks(ticks, expected);
+    }
+
 };
 
 #endif