comparison 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
comparison
equal deleted inserted replaced
550:d666f5f8b154 551:c2ba2796cbee
234 234
235 void 235 void
236 RegionLayer::recalcSpacing() 236 RegionLayer::recalcSpacing()
237 { 237 {
238 m_spacingMap.clear(); 238 m_spacingMap.clear();
239 m_distributionMap.clear();
239 if (!m_model) return; 240 if (!m_model) return;
240 241
241 std::cerr << "RegionLayer::recalcSpacing" << std::endl; 242 // std::cerr << "RegionLayer::recalcSpacing" << std::endl;
242
243 std::set<float> values;
244 243
245 for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin(); 244 for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin();
246 i != m_model->getPoints().end(); ++i) { 245 i != m_model->getPoints().end(); ++i) {
247 values.insert(i->value); 246 m_distributionMap[i->value]++;
248 std::cerr << "RegionLayer::recalcSpacing: value found: " << i->value << std::endl; 247 // std::cerr << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" << std::endl;
249 } 248 }
250 249
251 int n = 0; 250 int n = 0;
252 251
253 for (std::set<float>::const_iterator i = values.begin(); 252 for (SpacingMap::const_iterator i = m_distributionMap.begin();
254 i != values.end(); ++i) { 253 i != m_distributionMap.end(); ++i) {
255 m_spacingMap[*i] = n++; 254 m_spacingMap[i->first] = n++;
256 std::cerr << "RegionLayer::recalcSpacing: " << *i << " -> " << m_spacingMap[*i] << std::endl; 255 // std::cerr << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << std::endl;
257 } 256 }
258 } 257 }
259 258
260 bool 259 bool
261 RegionLayer::getValueExtents(float &min, float &max, 260 RegionLayer::getValueExtents(float &min, float &max,
573 RegionLayer::yToSpacingIndex(View *v, int y) const 572 RegionLayer::yToSpacingIndex(View *v, int y) const
574 { 573 {
575 // we return an inexact result here (float rather than int) 574 // we return an inexact result here (float rather than int)
576 int h = v->height(); 575 int h = v->height();
577 int n = m_spacingMap.size(); 576 int n = m_spacingMap.size();
578 // from y = h - ((h * i) / n) + (h / (2 * n)) as above 577 // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
579 float vh = ((h + (h / float(2 * n)) - y) * n) / h; 578 float vh = float(2*h*n - h - 2*n*y) / float(2*h);
580 return vh; 579 return vh;
581 } 580 }
582 581
583 int 582 int
584 RegionLayer::getYForValue(View *v, float val) const 583 RegionLayer::getYForValue(View *v, float val) const
594 SpacingMap::const_iterator i = m_spacingMap.lower_bound(val); 593 SpacingMap::const_iterator i = m_spacingMap.lower_bound(val);
595 //!!! what now, if i->first != v? 594 //!!! what now, if i->first != v?
596 595
597 int y = spacingIndexToY(v, i->second); 596 int y = spacingIndexToY(v, i->second);
598 597
599 std::cerr << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << std::endl; 598 // std::cerr << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << std::endl;
600 return y; 599 return y;
601 600
602 601
603 } else { 602 } else {
604 603
614 return int(h - ((val - min) * h) / (max - min)); 613 return int(h - ((val - min) * h) / (max - min));
615 } 614 }
616 } 615 }
617 616
618 float 617 float
619 RegionLayer::getValueForY(View *v, int y) const 618 RegionLayer::getValueForY(View *v, int y, int avoid) const
620 { 619 {
621 float min = 0.0, max = 0.0; 620 float min = 0.0, max = 0.0;
622 bool logarithmic = false; 621 bool logarithmic = false;
623 int h = v->height(); 622 int h = v->height();
624 623
647 int iy = spacingIndexToY(v, ivh); 646 int iy = spacingIndexToY(v, ivh);
648 647
649 int dist = iy - y; 648 int dist = iy - y;
650 int gap = h / n; // between region lines 649 int gap = h / n; // between region lines
651 650
652 std::cerr << "getValueForY: y = " << y << ", n = " << n << ", vh = " << vh << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl; 651 // std::cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl;
653 652
654 SpacingMap::const_iterator i = m_spacingMap.begin(); 653 SpacingMap::const_iterator i = m_spacingMap.begin();
655 while (i != m_spacingMap.end()) { 654 while (i != m_spacingMap.end()) {
656 if (i->second == ivh) break; 655 if (i->second == ivh) break;
657 ++i; 656 ++i;
658 } 657 }
659 if (i == m_spacingMap.end()) i = m_spacingMap.begin(); 658 if (i == m_spacingMap.end()) i = m_spacingMap.begin();
660 659
660 // std::cerr << "nearest existing value = " << i->first << " at " << iy << std::endl;
661
661 float val = 0; 662 float val = 0;
662 663
663 if (dist > -gap/3 && dist < gap/3) { 664 // std::cerr << "note: avoid = " << avoid << ", i->second = " << i->second << std::endl;
664 // snap 665
665 val = i->first; 666 if (dist < -gap/3 &&
666 std::cerr << "snapped to " << val << std::endl; 667 ((avoid == -1) ||
667 } else if (dist < 0) { 668 (avoid != i->second && avoid != i->second - 1))) {
668 // bisect gap to prior 669 // bisect gap to prior
669 if (i == m_spacingMap.begin()) { 670 if (i == m_spacingMap.begin()) {
670 val = i->first - 1.f; 671 val = i->first - 1.f;
671 std::cerr << "extended down to " << val << std::endl; 672 // std::cerr << "extended down to " << val << std::endl;
672 } else { 673 } else {
673 SpacingMap::const_iterator j = i; 674 SpacingMap::const_iterator j = i;
674 --j; 675 --j;
675 val = (i->first + j->first) / 2; 676 val = (i->first + j->first) / 2;
676 std::cerr << "bisected down to " << val << std::endl; 677 // std::cerr << "bisected down to " << val << std::endl;
677 } 678 }
678 } else { 679 } else if (dist > gap/3 &&
680 ((avoid == -1) ||
681 (avoid != i->second && avoid != i->second + 1))) {
679 // bisect gap to following 682 // bisect gap to following
680 SpacingMap::const_iterator j = i; 683 SpacingMap::const_iterator j = i;
681 ++j; 684 ++j;
682 if (j == m_spacingMap.end()) { 685 if (j == m_spacingMap.end()) {
683 val = i->first + 1.f; 686 val = i->first + 1.f;
684 std::cerr << "extended up to " << val << std::endl; 687 // std::cerr << "extended up to " << val << std::endl;
685 } else { 688 } else {
686 val = (i->first + j->first) / 2; 689 val = (i->first + j->first) / 2;
687 std::cerr << "bisected up to " << val << std::endl; 690 // std::cerr << "bisected up to " << val << std::endl;
688 } 691 }
692 } else {
693 // snap
694 val = i->first;
695 // std::cerr << "snapped to " << val << std::endl;
689 } 696 }
690 697
691 return val; 698 return val;
692 699
693 } else { 700 } else {
762 float min = m_model->getValueMinimum(); 769 float min = m_model->getValueMinimum();
763 float max = m_model->getValueMaximum(); 770 float max = m_model->getValueMaximum();
764 if (max == min) max = min + 1.0; 771 if (max == min) max = min + 1.0;
765 772
766 QPoint localPos; 773 QPoint localPos;
767 long illuminateFrame = -1; 774 RegionModel::Point illuminatePoint(0);
775 bool shouldIlluminate = false;
768 776
769 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 777 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
770 RegionModel::PointList localPoints = 778 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
771 getLocalPoints(v, localPos.x()); 779 illuminatePoint);
772 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
773 } 780 }
774 781
775 paint.save(); 782 paint.save();
776 paint.setRenderHint(QPainter::Antialiasing, false); 783 paint.setRenderHint(QPainter::Antialiasing, false);
777 784
779 //!!! be assigned to avoid overlaps 786 //!!! be assigned to avoid overlaps
780 787
781 //!!! if it does have distinct values, we should still ensure y 788 //!!! if it does have distinct values, we should still ensure y
782 //!!! coord is never completely flat on the top or bottom 789 //!!! coord is never completely flat on the top or bottom
783 790
784 int textY = 0;
785 if (m_plotStyle == PlotSegmentation) {
786 textY = v->getTextLabelHeight(this, paint);
787 }
788
789 int fontHeight = paint.fontMetrics().height(); 791 int fontHeight = paint.fontMetrics().height();
790 int fontAscent = paint.fontMetrics().ascent(); 792 int fontAscent = paint.fontMetrics().ascent();
791 793
792 for (RegionModel::PointList::const_iterator i = points.begin(); 794 for (RegionModel::PointList::const_iterator i = points.begin();
793 i != points.end(); ++i) { 795 i != points.end(); ++i) {
805 807
806 if (j != points.end()) { 808 if (j != points.end()) {
807 const RegionModel::Point &q(*j); 809 const RegionModel::Point &q(*j);
808 int nx = v->getXForFrame(q.frame); 810 int nx = v->getXForFrame(q.frame);
809 if (nx < ex) ex = nx; 811 if (nx < ex) ex = nx;
810 }
811
812 if (m_plotStyle != PlotSegmentation) {
813 textY = y - fontHeight + fontAscent;
814 if (textY < fontAscent + 1) {
815 textY = fontAscent + 1;
816 }
817 } 812 }
818 813
819 if (m_model->getValueQuantization() != 0.0) { 814 if (m_model->getValueQuantization() != 0.0) {
820 h = y - getYForValue(v, p.value + m_model->getValueQuantization()); 815 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
821 if (h < 3) h = 3; 816 if (h < 3) h = 3;
829 } else { 824 } else {
830 paint.setPen(getBaseQColor()); 825 paint.setPen(getBaseQColor());
831 paint.setBrush(brushColour); 826 paint.setBrush(brushColour);
832 } 827 }
833 828
829 bool illuminated = false;
830
834 if (m_plotStyle == PlotSegmentation) { 831 if (m_plotStyle == PlotSegmentation) {
835 832
836 if (ex <= x) continue; 833 if (ex <= x) continue;
837 834
838 if (illuminateFrame != p.frame && 835 if (!shouldIlluminate ||
839 (ex < x + 5 || x >= v->width() - 1)) { 836 // "illuminatePoint != p"
840 paint.setPen(Qt::NoPen); 837 RegionModel::Point::Comparator()(illuminatePoint, p) ||
841 } 838 RegionModel::Point::Comparator()(p, illuminatePoint)) {
839
840 // if (illuminateFrame != p.frame &&
841 // (ex < x + 5 || x >= v->width() - 1)) {
842 paint.setPen(Qt::NoPen);
843 }
842 844
843 paint.drawRect(x, -1, ex - x, v->height() + 1); 845 paint.drawRect(x, -1, ex - x, v->height() + 1);
844 846
845 } else { 847 } else {
846 848
847 if (illuminateFrame == p.frame) { 849 if (shouldIlluminate &&
848 if (localPos.y() >= y - h && localPos.y() < y) { 850 // "illuminatePoint == p"
849 paint.setPen(v->getForeground()); 851 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
850 paint.setBrush(v->getForeground()); 852 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
851 } 853
854 paint.setPen(v->getForeground());
855 paint.setBrush(v->getForeground());
856
857 QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
858 v->drawVisibleText(paint,
859 x - paint.fontMetrics().width(vlabel) - 2,
860 y + paint.fontMetrics().height()/2
861 - paint.fontMetrics().descent(),
862 vlabel, View::OutlinedText);
863
864 QString hlabel = RealTime::frame2RealTime
865 (p.frame, m_model->getSampleRate()).toText(true).c_str();
866 v->drawVisibleText(paint,
867 x,
868 y - h/2 - paint.fontMetrics().descent() - 2,
869 hlabel, View::OutlinedText);
870
871 illuminated = true;
852 } 872 }
853 873
854 paint.drawLine(x, y-1, x + w, y-1); 874 paint.drawLine(x, y-1, x + w, y-1);
855 paint.drawLine(x, y+1, x + w, y+1); 875 paint.drawLine(x, y+1, x + w, y+1);
856 paint.drawLine(x, y - h/2, x, y + h/2); 876 paint.drawLine(x, y - h/2, x, y + h/2);
857 paint.drawLine(x+w, y - h/2, x + w, y + h/2); 877 paint.drawLine(x+w, y - h/2, x + w, y + h/2);
858 } 878 }
859 879
860 QString label = p.label; 880 if (!illuminated) {
861 if (label == "") { 881 QString label = p.label;
862 label = QString("[%1]").arg(p.value); 882 if (label == "") {
863 } 883 label = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
864 v->drawVisibleText(paint, x + 5, textY, label, View::OutlinedText); 884 }
885
886 if (m_plotStyle != PlotSegmentation) {
887 v->drawVisibleText(paint,
888 x - paint.fontMetrics().width(label) - 2,
889 y + paint.fontMetrics().height()/2
890 - paint.fontMetrics().descent(),
891 label, View::OutlinedText);
892 } else {
893 v->drawVisibleText(paint,
894 x + 5,
895 v->getTextLabelHeight(this, paint),
896 label, View::OutlinedText);
897 }
898 }
865 } 899 }
866 900
867 paint.restore(); 901 paint.restore();
868 } 902 }
869 903
870 void 904 void
871 RegionLayer::drawStart(View *v, QMouseEvent *e) 905 RegionLayer::drawStart(View *v, QMouseEvent *e)
872 { 906 {
873 // std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
874
875 if (!m_model) return; 907 if (!m_model) return;
876 908
877 long frame = v->getFrameForX(e->x()); 909 long frame = v->getFrameForX(e->x());
878 if (frame < 0) frame = 0; 910 if (frame < 0) frame = 0;
879 frame = frame / m_model->getResolution() * m_model->getResolution(); 911 frame = frame / m_model->getResolution() * m_model->getResolution();
894 } 926 }
895 927
896 void 928 void
897 RegionLayer::drawDrag(View *v, QMouseEvent *e) 929 RegionLayer::drawDrag(View *v, QMouseEvent *e)
898 { 930 {
899 // std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
900
901 if (!m_model || !m_editing) return; 931 if (!m_model || !m_editing) return;
902 932
903 long frame = v->getFrameForX(e->x()); 933 long frame = v->getFrameForX(e->x());
904 if (frame < 0) frame = 0; 934 if (frame < 0) frame = 0;
905 frame = frame / m_model->getResolution() * m_model->getResolution(); 935 frame = frame / m_model->getResolution() * m_model->getResolution();
920 m_editingPoint.frame = newFrame; 950 m_editingPoint.frame = newFrame;
921 m_editingPoint.value = newValue; 951 m_editingPoint.value = newValue;
922 m_editingPoint.duration = newDuration; 952 m_editingPoint.duration = newDuration;
923 m_editingCommand->addPoint(m_editingPoint); 953 m_editingCommand->addPoint(m_editingPoint);
924 954
925 // recalcSpacing(); 955 recalcSpacing();
926 } 956 }
927 957
928 void 958 void
929 RegionLayer::drawEnd(View *, QMouseEvent *) 959 RegionLayer::drawEnd(View *, QMouseEvent *)
930 { 960 {
931 // std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
932 if (!m_model || !m_editing) return; 961 if (!m_model || !m_editing) return;
933 finish(m_editingCommand); 962 finish(m_editingCommand);
934 m_editingCommand = 0; 963 m_editingCommand = 0;
935 m_editing = false; 964 m_editing = false;
936 965
941 RegionLayer::eraseStart(View *v, QMouseEvent *e) 970 RegionLayer::eraseStart(View *v, QMouseEvent *e)
942 { 971 {
943 if (!m_model) return; 972 if (!m_model) return;
944 973
945 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 974 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
946 /* 975
947 RegionModel::PointList points = getLocalPoints(v, e->x());
948 if (points.empty()) return;
949
950 m_editingPoint = *points.begin();
951 */
952 if (m_editingCommand) { 976 if (m_editingCommand) {
953 finish(m_editingCommand); 977 finish(m_editingCommand);
954 m_editingCommand = 0; 978 m_editingCommand = 0;
955 } 979 }
956 980
967 RegionLayer::eraseEnd(View *v, QMouseEvent *e) 991 RegionLayer::eraseEnd(View *v, QMouseEvent *e)
968 { 992 {
969 if (!m_model || !m_editing) return; 993 if (!m_model || !m_editing) return;
970 994
971 m_editing = false; 995 m_editing = false;
972 /* 996
973 RegionModel::PointList points = getLocalPoints(v, e->x());
974 if (points.empty()) return;
975 if (points.begin()->frame != m_editingPoint.frame ||
976 points.begin()->value != m_editingPoint.value) return;
977 */
978 RegionModel::Point p(0); 997 RegionModel::Point p(0);
979 if (!getPointToDrag(v, e->x(), e->y(), p)) return; 998 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
980 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; 999 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
981 1000
982 m_editingCommand = new RegionModel::EditCommand 1001 m_editingCommand = new RegionModel::EditCommand
991 } 1010 }
992 1011
993 void 1012 void
994 RegionLayer::editStart(View *v, QMouseEvent *e) 1013 RegionLayer::editStart(View *v, QMouseEvent *e)
995 { 1014 {
996 std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
997
998 if (!m_model) return; 1015 if (!m_model) return;
999 1016
1000 // OrderedPointList opoints = getNearbyPoints(v, e->x(), e->y());
1001 // RegionLayer:
1002
1003 // RegionModel::PointList points = getLocalPoints(v, e->x());
1004 // if (points.empty()) return;
1005
1006 // m_editingPoint = *points.begin();
1007
1008 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) { 1017 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
1009 return; 1018 return;
1010 } 1019 }
1020
1021 m_dragPointX = v->getXForFrame(m_editingPoint.frame);
1022 m_dragPointY = getYForValue(v, m_editingPoint.value);
1011 1023
1012 m_originalPoint = m_editingPoint; 1024 m_originalPoint = m_editingPoint;
1013 1025
1014 if (m_editingCommand) { 1026 if (m_editingCommand) {
1015 finish(m_editingCommand); 1027 finish(m_editingCommand);
1016 m_editingCommand = 0; 1028 m_editingCommand = 0;
1017 } 1029 }
1018 1030
1019 m_editing = true; 1031 m_editing = true;
1020 m_dragYOrigin = e->y(); 1032 m_dragStartX = e->x();
1021 m_dragYRebase = e->y(); 1033 m_dragStartY = e->y();
1022 recalcSpacing(); 1034 recalcSpacing();
1023 } 1035 }
1024 1036
1025 void 1037 void
1026 RegionLayer::editDrag(View *v, QMouseEvent *e) 1038 RegionLayer::editDrag(View *v, QMouseEvent *e)
1027 { 1039 {
1028 std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
1029
1030 if (!m_model || !m_editing) return; 1040 if (!m_model || !m_editing) return;
1031 1041
1032 long frame = v->getFrameForX(e->x()); 1042 int xdist = e->x() - m_dragStartX;
1043 int ydist = e->y() - m_dragStartY;
1044 int newx = m_dragPointX + xdist;
1045 int newy = m_dragPointY + ydist;
1046
1047 long frame = v->getFrameForX(newx);
1033 if (frame < 0) frame = 0; 1048 if (frame < 0) frame = 0;
1034 frame = frame / m_model->getResolution() * m_model->getResolution(); 1049 frame = frame / m_model->getResolution() * m_model->getResolution();
1035 1050
1036 int activeY = e->y() + (m_dragYRebase - m_dragYOrigin); 1051 // Do not bisect between two values, if one of those values is
1037 float value = getValueForY(v, activeY); 1052 // that of the point we're actually moving ...
1053 int avoid = m_spacingMap[m_editingPoint.value];
1054
1055 // ... unless there are other points with the same value
1056 if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1;
1057
1058 float value = getValueForY(v, newy, avoid);
1038 1059
1039 if (!m_editingCommand) { 1060 if (!m_editingCommand) {
1040 m_editingCommand = new RegionModel::EditCommand(m_model, 1061 m_editingCommand = new RegionModel::EditCommand(m_model,
1041 tr("Drag Region")); 1062 tr("Drag Region"));
1042 }
1043
1044 if (m_verticalScale == EqualSpaced) {
1045 if (getYForValue(v, value) != getYForValue(v, m_editingPoint.value)) {
1046 m_dragYRebase = e->y();
1047 }
1048 } 1063 }
1049 1064
1050 m_editingCommand->deletePoint(m_editingPoint); 1065 m_editingCommand->deletePoint(m_editingPoint);
1051 m_editingPoint.frame = frame; 1066 m_editingPoint.frame = frame;
1052 m_editingPoint.value = value; 1067 m_editingPoint.value = value;
1055 } 1070 }
1056 1071
1057 void 1072 void
1058 RegionLayer::editEnd(View *, QMouseEvent *e) 1073 RegionLayer::editEnd(View *, QMouseEvent *e)
1059 { 1074 {
1060 std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
1061 if (!m_model || !m_editing) return; 1075 if (!m_model || !m_editing) return;
1062 1076
1063 if (m_editingCommand) { 1077 if (m_editingCommand) {
1064 1078
1065 QString newName = m_editingCommand->getName(); 1079 QString newName = m_editingCommand->getName();