Mercurial > hg > svgui
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 { |