comparison layer/RegionLayer.cpp @ 1432:5b9692768beb single-point

Further snap fixes
author Chris Cannam
date Wed, 20 Mar 2019 15:46:17 +0000
parents 8a7c82282fbc
children 696e569ff21b
comparison
equal deleted inserted replaced
1431:af824022bffd 1432:5b9692768beb
443 { 443 {
444 if (!m_model) { 444 if (!m_model) {
445 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 445 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
446 } 446 }
447 447
448 // SnapLeft / SnapRight: return frame of nearest feature in that
449 // direction no matter how far away
450 //
451 // SnapNeighbouring: return frame of feature that would be used in
452 // an editing operation, i.e. closest feature in either direction
453 // but only if it is "close enough"
454
448 resolution = m_model->getResolution(); 455 resolution = m_model->getResolution();
449 EventVector points;
450 456
451 if (snap == SnapNeighbouring) { 457 if (snap == SnapNeighbouring) {
452 458 EventVector points = getLocalPoints(v, v->getXForFrame(frame));
453 points = getLocalPoints(v, v->getXForFrame(frame));
454 if (points.empty()) return false; 459 if (points.empty()) return false;
455 frame = points.begin()->getFrame(); 460 frame = points.begin()->getFrame();
456 return true; 461 return true;
457 } 462 }
458 463
459 //!!! I think this is not quite right - we want to be able to snap 464 // Normally we snap to the start frame of whichever event we
460 //!!! to events that are nearby but not covering the given frame, 465 // find. However here, for SnapRight only, if the end frame of
461 //!!! and I think that worked with the old code. Compare and 466 // whichever event we would have snapped to had we been snapping
462 //!!! revise. 467 // left is closer than the start frame of the next event to the
468 // right, then we snap to that frame instead. Clear?
463 469
464 points = m_model->getEventsCovering(frame); 470 Event left;
465 sv_frame_t snapped = frame; 471 bool haveLeft = false;
466 bool found = false; 472 if (m_model->getNearestEventMatching
467 473 (frame, [](Event) { return true; }, EventSeries::Backward, left)) {
468 for (EventVector::const_iterator i = points.begin(); 474 haveLeft = true;
469 i != points.end(); ++i) { 475 }
470 476
471 if (snap == SnapRight) { 477 if (snap == SnapLeft) {
472 478 frame = left.getFrame();
473 // The best frame to snap to is the end frame of whichever 479 return haveLeft;
474 // feature we would have snapped to the start frame of if 480 }
475 // we had been snapping left. 481
476 482 Event right;
477 if (i->getFrame() <= frame) { 483 bool haveRight = false;
478 if (i->getFrame() + i->getDuration() > frame) { 484 if (m_model->getNearestEventMatching
479 snapped = i->getFrame() + i->getDuration(); 485 (frame, [](Event) { return true; }, EventSeries::Forward, right)) {
480 found = true; // don't break, as the next may be better 486 haveRight = true;
487 }
488
489 if (haveLeft) {
490 sv_frame_t leftEnd = left.getFrame() + left.getDuration();
491 if (leftEnd > frame) {
492 if (haveRight) {
493 if (leftEnd - frame < right.getFrame() - frame) {
494 frame = leftEnd;
495 } else {
496 frame = right.getFrame();
481 } 497 }
482 } else { 498 } else {
483 if (!found) { 499 frame = leftEnd;
484 snapped = i->getFrame();
485 found = true;
486 }
487 break;
488 } 500 }
489 501 return true;
490 } else if (snap == SnapLeft) { 502 }
491 503 }
492 if (i->getFrame() <= frame) { 504
493 snapped = i->getFrame(); 505 if (haveRight) {
494 found = true; // don't break, as the next may be better 506 frame = right.getFrame();
495 } else { 507 return true;
496 break; 508 }
497 } 509
498 510 return false;
499 } else { // nearest
500
501 EventVector::const_iterator j = i;
502 ++j;
503
504 if (j == points.end()) {
505
506 snapped = i->getFrame();
507 found = true;
508 break;
509
510 } else if (j->getFrame() >= frame) {
511
512 if (j->getFrame() - frame < frame - i->getFrame()) {
513 snapped = j->getFrame();
514 } else {
515 snapped = i->getFrame();
516 }
517 found = true;
518 break;
519 }
520 }
521 }
522
523 frame = snapped;
524 return found;
525 } 511 }
526 512
527 bool 513 bool
528 RegionLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, 514 RegionLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
529 int &resolution, 515 int &resolution,
531 { 517 {
532 if (!m_model) { 518 if (!m_model) {
533 return Layer::snapToSimilarFeature(v, frame, resolution, snap); 519 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
534 } 520 }
535 521
522 // snap is only permitted to be SnapLeft or SnapRight here. We
523 // don't do the same trick as in snapToFeatureFrame, of snapping
524 // to the end of a feature sometimes.
525
536 resolution = m_model->getResolution(); 526 resolution = m_model->getResolution();
537 527
538 /*!!! todo: overhaul the logic of this function (and supporting 528 Event ref;
539 apis in EventSeries / RegionModel) 529 Event e;
540 530 float matchvalue;
541 const EventVector &points = m_model->getPoints(); 531 bool found;
542 EventVector close = m_model->getPoints(frame, frame); 532
543 */ 533 found = m_model->getNearestEventMatching
544 EventVector points = m_model->getAllEvents(); 534 (frame, [](Event) { return true; }, EventSeries::Backward, ref);
545 EventVector close = {}; 535
536 if (!found) {
537 return false;
538 }
539
540 matchvalue = ref.getValue();
546 541
547 EventVector::const_iterator i; 542 found = m_model->getNearestEventMatching
548 543 (frame,
549 sv_frame_t matchframe = frame; 544 [matchvalue](Event e) {
550 double matchvalue = 0.f; 545 double epsilon = 0.0001;
551 546 return fabs(e.getValue() - matchvalue) < epsilon;
552 for (i = close.begin(); i != close.end(); ++i) { 547 },
553 if (i->getFrame() > frame) break; 548 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
554 matchvalue = i->getValue(); 549 e);
555 matchframe = i->getFrame(); 550
556 } 551 if (!found) {
557 552 return false;
558 sv_frame_t snapped = frame; 553 }
559 bool found = false; 554
560 bool distant = false; 555 frame = e.getFrame();
561 double epsilon = 0.0001; 556 return true;
562
563 i = close.begin();
564
565 // Scan through the close points first, then the more distant ones
566 // if no suitable close one is found. So the while-termination
567 // condition here can only happen once i has passed through the
568 // whole of the close container and then the whole of the separate
569 // points container. The two iterators are totally distinct, but
570 // have the same type so we cheekily use the same variable and a
571 // single loop for both.
572
573 while (i != points.end()) {
574
575 if (!distant) {
576 if (i == close.end()) {
577 // switch from the close container to the points container
578 i = points.begin();
579 distant = true;
580 }
581 }
582
583 if (snap == SnapRight) {
584
585 if (i->getFrame() > matchframe &&
586 fabs(i->getValue() - matchvalue) < epsilon) {
587 snapped = i->getFrame();
588 found = true;
589 break;
590 }
591
592 } else if (snap == SnapLeft) {
593
594 if (i->getFrame() < matchframe) {
595 if (fabs(i->getValue() - matchvalue) < epsilon) {
596 snapped = i->getFrame();
597 found = true; // don't break, as the next may be better
598 }
599 } else if (found || distant) {
600 break;
601 }
602
603 } else {
604 // no other snap types supported
605 }
606
607 ++i;
608 }
609
610 frame = snapped;
611 return found;
612 } 557 }
613 558
614 QString 559 QString
615 RegionLayer::getScaleUnits() const 560 RegionLayer::getScaleUnits() const
616 { 561 {