diff layer/RegionLayer.cpp @ 551:c2ba2796cbee sv-v1.7

* Big improvements to editing behaviour in note and region models
author Chris Cannam
date Fri, 02 Oct 2009 13:56:10 +0000
parents d666f5f8b154
children 2e8194a30f40
line wrap: on
line diff
--- a/layer/RegionLayer.cpp	Mon Sep 28 12:29:12 2009 +0000
+++ b/layer/RegionLayer.cpp	Fri Oct 02 13:56:10 2009 +0000
@@ -236,24 +236,23 @@
 RegionLayer::recalcSpacing()
 {
     m_spacingMap.clear();
+    m_distributionMap.clear();
     if (!m_model) return;
 
-    std::cerr << "RegionLayer::recalcSpacing" << std::endl;
-
-    std::set<float> values;
+//    std::cerr << "RegionLayer::recalcSpacing" << std::endl;
 
     for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin();
          i != m_model->getPoints().end(); ++i) {
-        values.insert(i->value);
-        std::cerr << "RegionLayer::recalcSpacing: value found: " << i->value << std::endl;
+        m_distributionMap[i->value]++;
+//        std::cerr << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" <<  std::endl;
     }
 
     int n = 0;
 
-    for (std::set<float>::const_iterator i = values.begin();
-         i != values.end(); ++i) {
-        m_spacingMap[*i] = n++;
-        std::cerr << "RegionLayer::recalcSpacing: " << *i << " -> " << m_spacingMap[*i] << std::endl;
+    for (SpacingMap::const_iterator i = m_distributionMap.begin();
+         i != m_distributionMap.end(); ++i) {
+        m_spacingMap[i->first] = n++;
+//        std::cerr << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << std::endl;
     }
 }
 
@@ -575,8 +574,8 @@
     // we return an inexact result here (float rather than int)
     int h = v->height();
     int n = m_spacingMap.size();
-    // from y = h - ((h * i) / n) + (h / (2 * n)) as above
-    float vh = ((h + (h / float(2 * n)) - y) * n) / h;
+    // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
+    float vh = float(2*h*n - h - 2*n*y) / float(2*h);
     return vh;
 }
 
@@ -596,7 +595,7 @@
 
         int y = spacingIndexToY(v, i->second);
 
-        std::cerr << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << std::endl;
+//        std::cerr << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << std::endl;
         return y;
 
 
@@ -616,7 +615,7 @@
 }
 
 float
-RegionLayer::getValueForY(View *v, int y) const
+RegionLayer::getValueForY(View *v, int y, int avoid) const
 {
     float min = 0.0, max = 0.0;
     bool logarithmic = false;
@@ -649,7 +648,7 @@
         int dist = iy - y;
         int gap = h / n; // between region lines
 
-        std::cerr << "getValueForY: y = " << y << ", n = " << n << ", vh = " << vh << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl;
+//        std::cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl;
 
         SpacingMap::const_iterator i = m_spacingMap.begin();
         while (i != m_spacingMap.end()) {
@@ -658,34 +657,42 @@
         }
         if (i == m_spacingMap.end()) i = m_spacingMap.begin();
 
+//        std::cerr << "nearest existing value = " << i->first << " at " << iy << std::endl;
+
         float val = 0;
 
-        if (dist > -gap/3 && dist < gap/3) {
-            // snap
-            val = i->first;
-            std::cerr << "snapped to " << val << std::endl;
-        } else if (dist < 0) {
+//        std::cerr << "note: avoid = " << avoid << ", i->second = " << i->second << std::endl;
+
+        if (dist < -gap/3 &&
+            ((avoid == -1) ||
+             (avoid != i->second && avoid != i->second - 1))) {
             // bisect gap to prior
             if (i == m_spacingMap.begin()) {
                 val = i->first - 1.f;
-                std::cerr << "extended down to " << val << std::endl;
+//                std::cerr << "extended down to " << val << std::endl;
             } else {
                 SpacingMap::const_iterator j = i;
                 --j;
                 val = (i->first + j->first) / 2;
-                std::cerr << "bisected down to " << val << std::endl;
+//                std::cerr << "bisected down to " << val << std::endl;
             }
-        } else {
+        } else if (dist > gap/3 &&
+                   ((avoid == -1) ||
+                    (avoid != i->second && avoid != i->second + 1))) {
             // bisect gap to following
             SpacingMap::const_iterator j = i;
             ++j;
             if (j == m_spacingMap.end()) {
                 val = i->first + 1.f;
-                std::cerr << "extended up to " << val << std::endl;
+//                std::cerr << "extended up to " << val << std::endl;
             } else {
                 val = (i->first + j->first) / 2;
-                std::cerr << "bisected up to " << val << std::endl;
+//                std::cerr << "bisected up to " << val << std::endl;
             }
+        } else {
+            // snap
+            val = i->first;
+//            std::cerr << "snapped to " << val << std::endl;
         }            
 
         return val;
@@ -764,12 +771,12 @@
     if (max == min) max = min + 1.0;
 
     QPoint localPos;
-    long illuminateFrame = -1;
+    RegionModel::Point illuminatePoint(0);
+    bool shouldIlluminate = false;
 
     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
-	RegionModel::PointList localPoints =
-	    getLocalPoints(v, localPos.x());
-	if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
+        shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
+                                          illuminatePoint);
     }
 
     paint.save();
@@ -781,11 +788,6 @@
     //!!! if it does have distinct values, we should still ensure y
     //!!! coord is never completely flat on the top or bottom
 
-    int textY = 0;
-    if (m_plotStyle == PlotSegmentation) {
-        textY = v->getTextLabelHeight(this, paint);
-    }
-    
     int fontHeight = paint.fontMetrics().height();
     int fontAscent = paint.fontMetrics().ascent();
 
@@ -809,13 +811,6 @@
             if (nx < ex) ex = nx;
         }
 
-        if (m_plotStyle != PlotSegmentation) {
-            textY = y - fontHeight + fontAscent;
-            if (textY < fontAscent + 1) {
-                textY = fontAscent + 1;
-            }
-        }
-
 	if (m_model->getValueQuantization() != 0.0) {
 	    h = y - getYForValue(v, p.value + m_model->getValueQuantization());
 	    if (h < 3) h = 3;
@@ -831,37 +826,76 @@
             paint.setBrush(brushColour);
         }
 
+        bool illuminated = false;
+
 	if (m_plotStyle == PlotSegmentation) {
 
 	    if (ex <= x) continue;
 
-	    if (illuminateFrame != p.frame &&
-		(ex < x + 5 || x >= v->width() - 1)) {
-		paint.setPen(Qt::NoPen);
-	    }
+            if (!shouldIlluminate ||
+                // "illuminatePoint != p"
+                RegionModel::Point::Comparator()(illuminatePoint, p) ||
+                RegionModel::Point::Comparator()(p, illuminatePoint)) {
+
+//	    if (illuminateFrame != p.frame &&
+//		(ex < x + 5 || x >= v->width() - 1)) {
+                paint.setPen(Qt::NoPen);
+            }
 
 	    paint.drawRect(x, -1, ex - x, v->height() + 1);
 
 	} else {
 
-            if (illuminateFrame == p.frame) {
-                if (localPos.y() >= y - h && localPos.y() < y) {
-                    paint.setPen(v->getForeground());
-                    paint.setBrush(v->getForeground());
-                }
+            if (shouldIlluminate &&
+                // "illuminatePoint == p"
+                !RegionModel::Point::Comparator()(illuminatePoint, p) &&
+                !RegionModel::Point::Comparator()(p, illuminatePoint)) {
+
+                paint.setPen(v->getForeground());
+                paint.setBrush(v->getForeground());
+
+                QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
+                v->drawVisibleText(paint, 
+                                   x - paint.fontMetrics().width(vlabel) - 2,
+                                   y + paint.fontMetrics().height()/2
+                                   - paint.fontMetrics().descent(), 
+                                   vlabel, View::OutlinedText);
+                
+                QString hlabel = RealTime::frame2RealTime
+                    (p.frame, m_model->getSampleRate()).toText(true).c_str();
+                v->drawVisibleText(paint, 
+                                   x,
+                                   y - h/2 - paint.fontMetrics().descent() - 2,
+                                   hlabel, View::OutlinedText);
+
+                illuminated = true;
             }
-	
+            
             paint.drawLine(x, y-1, x + w, y-1);
             paint.drawLine(x, y+1, x + w, y+1);
             paint.drawLine(x, y - h/2, x, y + h/2);
             paint.drawLine(x+w, y - h/2, x + w, y + h/2);
         }
 
-        QString label = p.label;
-	if (label == "") {
-            label = QString("[%1]").arg(p.value);
+        if (!illuminated) {
+            QString label = p.label;
+            if (label == "") {
+                label = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
+            }
+
+            if (m_plotStyle != PlotSegmentation) {
+                v->drawVisibleText(paint,
+                                   x - paint.fontMetrics().width(label) - 2,
+                                   y + paint.fontMetrics().height()/2
+                                     - paint.fontMetrics().descent(), 
+                                   label, View::OutlinedText);
+            } else {
+                v->drawVisibleText(paint,
+                                   x + 5,
+                                   v->getTextLabelHeight(this, paint),
+                                   label, View::OutlinedText);
+            }
         }
-        v->drawVisibleText(paint, x + 5, textY, label, View::OutlinedText);
     }
 
     paint.restore();
@@ -870,8 +904,6 @@
 void
 RegionLayer::drawStart(View *v, QMouseEvent *e)
 {
-//    std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model) return;
 
     long frame = v->getFrameForX(e->x());
@@ -896,8 +928,6 @@
 void
 RegionLayer::drawDrag(View *v, QMouseEvent *e)
 {
-//    std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model || !m_editing) return;
 
     long frame = v->getFrameForX(e->x());
@@ -922,13 +952,12 @@
     m_editingPoint.duration = newDuration;
     m_editingCommand->addPoint(m_editingPoint);
 
-//    recalcSpacing();
+    recalcSpacing();
 }
 
 void
 RegionLayer::drawEnd(View *, QMouseEvent *)
 {
-//    std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
     finish(m_editingCommand);
     m_editingCommand = 0;
@@ -943,12 +972,7 @@
     if (!m_model) return;
 
     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
-/*
-    RegionModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return;
 
-    m_editingPoint = *points.begin();
-*/
     if (m_editingCommand) {
 	finish(m_editingCommand);
 	m_editingCommand = 0;
@@ -969,12 +993,7 @@
     if (!m_model || !m_editing) return;
 
     m_editing = false;
-/*
-    RegionModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return;
-    if (points.begin()->frame != m_editingPoint.frame ||
-        points.begin()->value != m_editingPoint.value) return;
-*/
+
     RegionModel::Point p(0);
     if (!getPointToDrag(v, e->x(), e->y(), p)) return;
     if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
@@ -993,22 +1012,15 @@
 void
 RegionLayer::editStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model) return;
 
-//    OrderedPointList opoints = getNearbyPoints(v, e->x(), e->y());
-//    RegionLayer:
-
-//    RegionModel::PointList points = getLocalPoints(v, e->x());
-//    if (points.empty()) return;
-
-//    m_editingPoint = *points.begin();
-    
     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
         return;
     }
 
+    m_dragPointX = v->getXForFrame(m_editingPoint.frame);
+    m_dragPointY = getYForValue(v, m_editingPoint.value);
+
     m_originalPoint = m_editingPoint;
 
     if (m_editingCommand) {
@@ -1017,36 +1029,39 @@
     }
 
     m_editing = true;
-    m_dragYOrigin = e->y();
-    m_dragYRebase = e->y();
+    m_dragStartX = e->x();
+    m_dragStartY = e->y();
     recalcSpacing();
 }
 
 void
 RegionLayer::editDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model || !m_editing) return;
 
-    long frame = v->getFrameForX(e->x());
+    int xdist = e->x() - m_dragStartX;
+    int ydist = e->y() - m_dragStartY;
+    int newx = m_dragPointX + xdist;
+    int newy = m_dragPointY + ydist;
+
+    long frame = v->getFrameForX(newx);
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
-    int activeY = e->y() + (m_dragYRebase - m_dragYOrigin);
-    float value = getValueForY(v, activeY);
+    // Do not bisect between two values, if one of those values is
+    // that of the point we're actually moving ...
+    int avoid = m_spacingMap[m_editingPoint.value];
+
+    // ... unless there are other points with the same value
+    if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1;
+
+    float value = getValueForY(v, newy, avoid);
 
     if (!m_editingCommand) {
 	m_editingCommand = new RegionModel::EditCommand(m_model,
 						      tr("Drag Region"));
     }
 
-    if (m_verticalScale == EqualSpaced) {
-        if (getYForValue(v, value) != getYForValue(v, m_editingPoint.value)) {
-            m_dragYRebase = e->y();
-        }
-    }
-
     m_editingCommand->deletePoint(m_editingPoint);
     m_editingPoint.frame = frame;
     m_editingPoint.value = value;
@@ -1057,7 +1072,6 @@
 void
 RegionLayer::editEnd(View *, QMouseEvent *e)
 {
-    std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
 
     if (m_editingCommand) {