changeset 1341:ab2cafd3a7cb zoom

Fixes for TimeRuler spacing and for the boundaries of the WaveformLayer paint area
author Chris Cannam
date Thu, 27 Sep 2018 15:20:25 +0100
parents fc3c9971a43a
children ed6400d5b571
files layer/TimeRulerLayer.cpp layer/TimeRulerLayer.h layer/WaveformLayer.cpp view/View.cpp
diffstat 4 files changed, 112 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/layer/TimeRulerLayer.cpp	Wed Sep 26 13:03:16 2018 +0100
+++ b/layer/TimeRulerLayer.cpp	Thu Sep 27 15:20:25 2018 +0100
@@ -59,8 +59,8 @@
     }
 
     bool q;
-    int tick = getMajorTickSpacing(v, q);
-    RealTime rtick = RealTime::fromMilliseconds(tick);
+    int tickUSec = getMajorTickUSec(v, q);
+    RealTime rtick = RealTime(0, tickUSec * 1000);
     sv_samplerate_t rate = m_model->getSampleRate();
     
     RealTime rt = RealTime::frame2RealTime(frame, rate);
@@ -142,19 +142,19 @@
 }
 
 int
-TimeRulerLayer::getMajorTickSpacing(LayerGeometryProvider *v, bool &quarterTicks) const
+TimeRulerLayer::getMajorTickUSec(LayerGeometryProvider *v,
+                                 bool &quarterTicks) const
 {
-    // return value is in milliseconds
-
-    if (!m_model || !v) return 1000;
+    // return value is in microseconds
+    if (!m_model || !v) return 1000 * 1000;
 
     sv_samplerate_t sampleRate = m_model->getSampleRate();
-    if (!sampleRate) return 1000;
+    if (!sampleRate) return 1000 * 1000;
 
     sv_frame_t startFrame = v->getStartFrame();
     sv_frame_t endFrame = v->getEndFrame();
 
-    int minPixelSpacing = 50;
+    int minPixelSpacing = ViewManager::scalePixelSize(50);
 
     RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
     RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
@@ -163,35 +163,74 @@
     if (count < 1) count = 1;
     RealTime rtGap = (rtEnd - rtStart) / count;
 
-    int incms;
+    int incus;
     quarterTicks = false;
 
     if (rtGap.sec > 0) {
-        incms = 1000;
+        incus = 1000 * 1000;
         int s = rtGap.sec;
-        if (s > 0) { incms *= 5; s /= 5; }
-        if (s > 0) { incms *= 2; s /= 2; }
-        if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
-        if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; }
-        if (s > 0) { incms *= 2; s /= 2; }
-        if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
+        if (s > 0) { incus *= 5; s /= 5; }
+        if (s > 0) { incus *= 2; s /= 2; }
+        if (s > 0) { incus *= 6; s /= 6; quarterTicks = true; }
+        if (s > 0) { incus *= 5; s /= 5; quarterTicks = false; }
+        if (s > 0) { incus *= 2; s /= 2; }
+        if (s > 0) { incus *= 6; s /= 6; quarterTicks = true; }
         while (s > 0) {
-            incms *= 10;
+            incus *= 10;
             s /= 10;
             quarterTicks = false;
         }
+    } else if (rtGap.msec() > 0) {
+        incus = 1000;
+        int ms = rtGap.msec();
+        if (ms > 0) { incus *= 10; ms /= 10; }
+        if (ms > 0) { incus *= 10; ms /= 10; }
+        if (ms > 0) { incus *= 5; ms /= 5; }
+        if (ms > 0) { incus *= 2; ms /= 2; }
     } else {
-        incms = 1;
-        int ms = rtGap.msec();
-//        cerr << "rtGap.msec = " << ms << ", rtGap = " << rtGap << ", count = " << count << endl;
-//        cerr << "startFrame = " << startFrame << ", endFrame = " << endFrame << " rtStart = " << rtStart << ", rtEnd = " << rtEnd << endl;
-        if (ms > 0) { incms *= 10; ms /= 10; }
-        if (ms > 0) { incms *= 10; ms /= 10; }
-        if (ms > 0) { incms *= 5; ms /= 5; }
-        if (ms > 0) { incms *= 2; ms /= 2; }
+        incus = 1;
+        int us = rtGap.usec();
+        if (us > 0) { incus *= 10; us /= 10; }
+        if (us > 0) { incus *= 10; us /= 10; }
+        if (us > 0) { incus *= 5; us /= 5; }
+        if (us > 0) { incus *= 2; us /= 2; }
     }
 
-    return incms;
+    return incus;
+}
+
+int
+TimeRulerLayer::getXForUSec(LayerGeometryProvider *v, double us) const
+{
+    sv_samplerate_t sampleRate = m_model->getSampleRate();
+    double dframe = (us * sampleRate) / 1000000.0;
+    double eps = 1e-7;
+    sv_frame_t frame = sv_frame_t(floor(dframe + eps));
+    int x;
+
+    ZoomLevel zoom = v->getZoomLevel();
+
+    if (zoom.zone == ZoomLevel::FramesPerPixel) {
+            
+        frame /= zoom.level;
+        frame *= zoom.level; // so frame corresponds to an exact pixel
+        
+        x = v->getXForFrame(frame);
+        
+    } else {
+
+        double off = dframe - double(frame);
+        int x0 = v->getXForFrame(frame);
+        int x1 = v->getXForFrame(frame + 1);
+        
+        x = int(x0 + off * (x1 - x0));
+    }
+
+#ifdef DEBUG_TIME_RULER_LAYER
+    cerr << "Considering frame = " << frame << ", x = " << x << endl;
+#endif
+        
+    return x;
 }
 
 void
@@ -214,22 +253,23 @@
 #endif
 
     bool quarter = false;
-    int incms = getMajorTickSpacing(v, quarter);
+    int incus = getMajorTickUSec(v, quarter);
 
-    int ms = int(lrint(1000.0 * (double(startFrame) / double(sampleRate))));
-    ms = (ms / incms) * incms - incms;
+    int us = int(lrint(1000.0 * 1000.0 * (double(startFrame) /
+                                          double(sampleRate))));
+    us = (us / incus) * incus - incus;
 
 #ifdef DEBUG_TIME_RULER_LAYER
-    cerr << "start ms = " << ms << " at step " << incms << endl;
+    cerr << "start us = " << us << " at step " << incus << endl;
 #endif
 
     // Calculate the number of ticks per increment -- approximate
     // values for x and frame counts here will do, no rounding issue.
-    // We always use the exact incms in our calculations for where to
+    // We always use the exact incus in our calculations for where to
     // draw the actual ticks or lines.
 
     int minPixelSpacing = 50;
-    sv_frame_t incFrame = lrint((incms * sampleRate) / 1000);
+    sv_frame_t incFrame = lrint((incus * sampleRate) / 1000000);
     int incX = int(round(v->getZoomLevel().framesToPixels(double(incFrame))));
     int ticks = 10;
     if (incX < minPixelSpacing * 2) {
@@ -242,10 +282,7 @@
 
     // Do not label time zero - we now overlay an opaque area over
     // time < 0 which would cut it in half
-    int minlabel = 1; // ms
-
-    // used for a sanity check
-    sv_frame_t prevframe = 0;
+    int minlabel = 1; // us
     
     while (1) {
 
@@ -254,26 +291,9 @@
         // a different pixel when scrolling a small amount and
         // re-drawing with a different start frame).
 
-        double dms = ms;
-        sv_frame_t frame = lrint((dms * sampleRate) / 1000.0);
-        ZoomLevel zoom = v->getZoomLevel();
-        if (zoom.zone == ZoomLevel::FramesPerPixel) {
-            frame /= zoom.level;
-            frame *= zoom.level; // so frame corresponds to an exact pixel
-        }
+        double dus = us;
 
-        if (frame == prevframe && prevframe != 0) {
-            cerr << "ERROR: frame == prevframe (== " << frame
-                 << ") in TimeRulerLayer::paint" << endl;
-            throw std::logic_error("frame == prevframe in TimeRulerLayer::paint");
-        }
-        prevframe = frame;
-        
-        int x = v->getXForFrame(frame);
-
-#ifdef DEBUG_TIME_RULER_LAYER
-        cerr << "Considering frame = " << frame << ", x = " << x << endl;
-#endif
+        int x = getXForUSec(v, dus);
 
         if (x >= rect.x() + rect.width() + 50) {
 #ifdef DEBUG_TIME_RULER_LAYER
@@ -282,9 +302,9 @@
             break;
         }
 
-        if (x >= rect.x() - 50 && ms >= minlabel) {
+        if (x >= rect.x() - 50 && us >= minlabel) {
 
-            RealTime rt = RealTime::fromMilliseconds(ms);
+            RealTime rt = RealTime(0, us * 1000);
 
 #ifdef DEBUG_TIME_RULER_LAYER
             cerr << "X in range, drawing line here for time " << rt.toText() << endl;
@@ -338,14 +358,9 @@
 
         for (int i = 1; i < ticks; ++i) {
 
-            dms = ms + (i * double(incms)) / ticks;
-            frame = lrint((dms * sampleRate) / 1000.0);
-            if (zoom.zone == ZoomLevel::FramesPerPixel) {
-                frame /= zoom.level;
-                frame *= zoom.level; // exact pixel as above
-            } 
+            dus = us + (i * double(incus)) / ticks;
 
-            x = v->getXForFrame(frame);
+            x = getXForUSec(v, dus);
 
             if (x < rect.x() || x >= rect.x() + rect.width()) {
 #ifdef DEBUG_TIME_RULER_LAYER
@@ -372,7 +387,7 @@
             paint.drawLine(x, v->getPaintHeight() - sz - 1, x, v->getPaintHeight() - 1);
         }
 
-        ms += incms;
+        us += incus;
     }
 
     paint.restore();
--- a/layer/TimeRulerLayer.h	Wed Sep 26 13:03:16 2018 +0100
+++ b/layer/TimeRulerLayer.h	Thu Sep 27 15:20:25 2018 +0100
@@ -68,7 +68,8 @@
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    int getMajorTickSpacing(LayerGeometryProvider *, bool &quarterTicks) const;
+    int getMajorTickUSec(LayerGeometryProvider *, bool &quarterTicks) const;
+    int getXForUSec(LayerGeometryProvider *, double usec) const;
 };
 
 #endif
--- a/layer/WaveformLayer.cpp	Wed Sep 26 13:03:16 2018 +0100
+++ b/layer/WaveformLayer.cpp	Thu Sep 27 15:20:25 2018 +0100
@@ -427,11 +427,14 @@
     f0 = f0 / modelZoomLevel;
     f0 = f0 * modelZoomLevel;
 
-    viewFrame = v->getFrameForX(x + 1);
-    
-    f1 = viewFrame;
-    f1 = f1 / modelZoomLevel;
-    f1 = f1 * modelZoomLevel;
+    if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
+        f1 = f0 + 1;
+    } else {
+        viewFrame = v->getFrameForX(x + 1);
+        f1 = viewFrame;
+        f1 = f1 / modelZoomLevel;
+        f1 = f1 * modelZoomLevel;
+    }
     
     return (f0 < m_model->getEndFrame());
 }
--- a/view/View.cpp	Wed Sep 26 13:03:16 2018 +0100
+++ b/view/View.cpp	Thu Sep 27 15:20:25 2018 +0100
@@ -381,15 +381,25 @@
 int
 View::getXForFrame(sv_frame_t frame) const
 {
+    // In FramesPerPixel mode, the pixel should be the one "covering"
+    // the given frame, i.e. to the "left" of it - not necessarily the
+    // nearest boundary.
+    
+    sv_frame_t level = m_zoomLevel.level;
     sv_frame_t fdiff = frame - getCentreFrame();
+    int diff, result;
 
     if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
-        fdiff /= m_zoomLevel.level;
+        diff = fdiff / level;
+        if ((fdiff < 0) && ((fdiff % level) != 0)) {
+            --diff; // round to the left
+        }
     } else {
-        fdiff *= m_zoomLevel.level;
+        diff = fdiff * level;
     }
 
-    return int(fdiff + (width()/2));
+    result = int(diff + (width()/2));
+    return result;
 }
 
 sv_frame_t
@@ -397,7 +407,10 @@
 {
     // Note, this must always return a value that is on a zoom-level
     // boundary - regardless of whether the nominal centre frame is on
-    // such a boundary or not
+    // such a boundary or not.
+    
+    // In PixelsPerFrame mode, the frame should be the one immediately
+    // left of the given pixel, not necessarily the nearest.
 
     int diff = x - (width()/2);
     sv_frame_t level = m_zoomLevel.level;
@@ -408,9 +421,12 @@
         result = ((fdiff + m_centreFrame) / level) * level;
     } else {
         fdiff = diff / level;
+        if ((diff < 0) && ((diff % level) != 0)) {
+            --fdiff; // round to the left
+        }
         result = fdiff + m_centreFrame;
     }
-/*    
+
     if (x == 0) {
         SVCERR << "getFrameForX(" << x << "): diff = " << diff << ", fdiff = "
                << fdiff << ", m_centreFrame = " << m_centreFrame
@@ -420,7 +436,7 @@
                << ", will return " << result
                << endl;
     }
-*/        
+
     return result;
 }