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