comparison layer/TimeValueLayer.cpp @ 1431:af824022bffd single-point

Begin fixing the various snap operations. Also remove SnapNearest, which is never used and seems to consume more lines of code than the rest!
author Chris Cannam
date Wed, 20 Mar 2019 14:59:34 +0000
parents 31499c3520ee
children 5b9692768beb
comparison
equal deleted inserted replaced
1430:31499c3520ee 1431:af824022bffd
534 EventVector 534 EventVector
535 TimeValueLayer::getLocalPoints(LayerGeometryProvider *v, int x) const 535 TimeValueLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
536 { 536 {
537 if (!m_model) return {}; 537 if (!m_model) return {};
538 538
539 // Return all points at a frame f, where f is the closest frame to
540 // pixel coordinate x whose pixel coordinate is both within a
541 // small (but somewhat arbitrary) fuzz distance from x and within
542 // the current view. If there is no such frame, return an empty
543 // vector.
544
539 sv_frame_t frame = v->getFrameForX(x); 545 sv_frame_t frame = v->getFrameForX(x);
540
541 //!!! this is not going to be right - we want "nearby" points and
542 //!!! there's no api for that
543 546
544 EventVector local = m_model->getEventsCovering(frame); 547 EventVector exact = m_model->getEventsStartingAt(frame);
545 if (!local.empty()) return local; 548 if (!exact.empty()) return exact;
546 549
547 return {}; 550 // overspill == 1, so one event either side of the given span
548 /*!!! 551 EventVector neighbouring = m_model->getEventsWithin
549 552 (frame, m_model->getResolution(), 1);
550 SparseTimeValueModel::PointList prevPoints = 553
551 m_model->getPreviousPoints(frame); 554 double fuzz = v->scaleSize(2);
552 SparseTimeValueModel::PointList nextPoints = 555 sv_frame_t suitable = 0;
553 m_model->getNextPoints(frame); 556 bool have = false;
554 557
555 SparseTimeValueModel::PointList usePoints = prevPoints; 558 for (Event e: neighbouring) {
556 559 sv_frame_t f = e.getFrame();
557 if (prevPoints.empty()) { 560 if (f < v->getStartFrame() || f > v->getEndFrame()) {
558 usePoints = nextPoints; 561 continue;
559 } else if (nextPoints.empty()) { 562 }
560 // stick with prevPoints 563 int px = v->getXForFrame(f);
561 } else if (prevPoints.begin()->frame < v->getStartFrame() && 564 if ((px > x && px - x > fuzz) || (px < x && x - px > fuzz + 3)) {
562 !(nextPoints.begin()->frame > v->getEndFrame())) { 565 continue;
563 usePoints = nextPoints; 566 }
564 } else if (nextPoints.begin()->frame - frame < 567 if (!have) {
565 frame - prevPoints.begin()->frame) { 568 suitable = f;
566 usePoints = nextPoints; 569 have = true;
567 } 570 } else if (llabs(frame - f) < llabs(suitable - f)) {
568 571 suitable = f;
569 if (!usePoints.empty()) { 572 }
570 double fuzz = v->scaleSize(2); 573 }
571 int px = v->getXForFrame(usePoints.begin()->frame); 574
572 if ((px > x && px - x > fuzz) || 575 if (have) {
573 (px < x && x - px > fuzz + 3)) { 576 return m_model->getEventsStartingAt(suitable);
574 usePoints.clear(); 577 } else {
575 } 578 return {};
576 } 579 }
577
578 return usePoints;
579 */
580 } 580 }
581 581
582 QString 582 QString
583 TimeValueLayer::getLabelPreceding(sv_frame_t /* frame */) const 583 TimeValueLayer::getLabelPreceding(sv_frame_t frame) const
584 { 584 {
585 if (!m_model || !m_model->hasTextLabels()) return ""; 585 if (!m_model || !m_model->hasTextLabels()) return "";
586 /*!!! no corresponding api yet 586
587 EventVector points = m_model->getPreviousPoints(frame); 587 Event e;
588 for (EventVector::const_iterator i = points.begin(); 588 if (m_model->getNearestEventMatching
589 i != points.end(); ++i) { 589 (frame,
590 if (i->getLabel() != "") return i->getLabel(); 590 [](Event e) { return e.hasLabel() && e.getLabel() != ""; },
591 } 591 EventSeries::Backward,
592 */ 592 e)) {
593 return e.getLabel();
594 }
595
593 return ""; 596 return "";
594 } 597 }
595 598
596 QString 599 QString
597 TimeValueLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const 600 TimeValueLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
655 { 658 {
656 if (!m_model) { 659 if (!m_model) {
657 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 660 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
658 } 661 }
659 662
663 // SnapLeft / SnapRight: return frame of nearest feature in that
664 // direction no matter how far away
665 //
666 // SnapNearest: return frame of nearest feature in either
667 // direction no matter how far away - I'm fairly sure this is
668 // never actually used
669 //
670 // SnapNeighbouring: return frame of feature that would be used in
671 // an editing operation, i.e. closest feature in either direction
672 // but only if it is "close enough"
673
660 resolution = m_model->getResolution(); 674 resolution = m_model->getResolution();
661 EventVector points;
662 675
663 if (snap == SnapNeighbouring) { 676 if (snap == SnapNeighbouring) {
664 points = getLocalPoints(v, v->getXForFrame(frame)); 677 EventVector points = getLocalPoints(v, v->getXForFrame(frame));
665 if (points.empty()) return false; 678 if (points.empty()) return false;
666 frame = points.begin()->getFrame(); 679 frame = points.begin()->getFrame();
667 return true; 680 return true;
668 } 681 }
669 682
670 //!!! again, wrong api - correct one is not here yet 683 Event e;
671 points = m_model->getEventsCovering(frame); 684 if (m_model->getNearestEventMatching
672 sv_frame_t snapped = frame; 685 (frame,
673 bool found = false; 686 [](Event) { return true; },
674 687 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
675 for (EventVector::const_iterator i = points.begin(); 688 e)) {
676 i != points.end(); ++i) { 689 frame = e.getFrame();
677 690 return true;
678 if (snap == SnapRight) { 691 }
679 692
680 if (i->getFrame() > frame) { 693 return false;
681 snapped = i->getFrame();
682 found = true;
683 break;
684 }
685
686 } else if (snap == SnapLeft) {
687
688 if (i->getFrame() <= frame) {
689 snapped = i->getFrame();
690 found = true; // don't break, as the next may be better
691 } else {
692 break;
693 }
694
695 } else { // nearest
696
697 EventVector::const_iterator j = i;
698 ++j;
699
700 if (j == points.end()) {
701
702 snapped = i->getFrame();
703 found = true;
704 break;
705
706 } else if (j->getFrame() >= frame) {
707
708 if (j->getFrame() - frame < frame - i->getFrame()) {
709 snapped = j->getFrame();
710 } else {
711 snapped = i->getFrame();
712 }
713 found = true;
714 break;
715 }
716 }
717 }
718
719 frame = snapped;
720 return found;
721 } 694 }
722 695
723 bool 696 bool
724 TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, 697 TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v,
698 sv_frame_t &frame,
725 int &resolution, 699 int &resolution,
726 SnapType snap) const 700 SnapType snap) const
727 { 701 {
728 if (!m_model) { 702 if (!m_model) {
729 return Layer::snapToSimilarFeature(v, frame, resolution, snap); 703 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
730 } 704 }
731 705
732 resolution = m_model->getResolution(); 706 resolution = m_model->getResolution();
733 707
734 /*!!! todo: overhaul the logic of this function (and supporting 708 Event ref;
735 apis in EventSeries / SparseTimeValueModel) 709 Event e;
736 710 float matchvalue;
737 const EventVector &points = m_model->getPoints(); 711 bool found;
738 EventVector close = m_model->getPoints(frame, frame); 712
739 */ 713 found = m_model->getNearestEventMatching
740 const EventVector &points = m_model->getAllEvents(); 714 (frame, [](Event) { return true; }, EventSeries::Backward, ref);
741 EventVector close = {}; 715
742 716 if (!found) {
743 EventVector::const_iterator i; 717 return false;
744 718 }
745 sv_frame_t matchframe = frame; 719
746 double matchvalue = 0.0; 720 matchvalue = ref.getValue();
747 721
748 for (i = close.begin(); i != close.end(); ++i) { 722 found = m_model->getNearestEventMatching
749 if (i->getFrame() > frame) break; 723 (frame,
750 matchvalue = i->getValue(); 724 [matchvalue](Event e) {
751 matchframe = i->getFrame(); 725 double epsilon = 0.0001;
752 } 726 return fabs(e.getValue() - matchvalue) < epsilon;
753 727 },
754 sv_frame_t snapped = frame; 728 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
755 bool found = false; 729 e);
756 bool distant = false; 730
757 double epsilon = 0.0001; 731 if (!found) {
758 732 return false;
759 i = close.begin(); 733 }
760 734
761 // Scan through the close points first, then the more distant ones 735 frame = e.getFrame();
762 // if no suitable close one is found. So the while-termination 736 return true;
763 // condition here can only happen once i has passed through the
764 // whole of the close container and then the whole of the separate
765 // points container. The two iterators are totally distinct, but
766 // have the same type so we cheekily use the same variable and a
767 // single loop for both.
768
769 while (i != points.end()) {
770
771 if (!distant) {
772 if (i == close.end()) {
773 // switch from the close container to the points container
774 i = points.begin();
775 distant = true;
776 }
777 }
778
779 if (snap == SnapRight) {
780
781 if (i->getFrame() > matchframe &&
782 fabs(i->getValue() - matchvalue) < epsilon) {
783 snapped = i->getFrame();
784 found = true;
785 break;
786 }
787
788 } else if (snap == SnapLeft) {
789
790 if (i->getFrame() < matchframe) {
791 if (fabs(i->getValue() - matchvalue) < epsilon) {
792 snapped = i->getFrame();
793 found = true; // don't break, as the next may be better
794 }
795 } else if (found || distant) {
796 break;
797 }
798
799 } else {
800 // no other snap types supported
801 }
802
803 ++i;
804 }
805
806 frame = snapped;
807 return found;
808 } 737 }
809 738
810 void 739 void
811 TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const 740 TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
812 { 741 {