comparison layer/BoxLayer.cpp @ 1547:e6362cf5ff1d

Pass a y-coord, optionally, to Layer::snapToFeatureFrame. This is necessary for BoxLayer which needs to coordinate its snaps with the box it is highlighting for editing. Then in BoxLayer, merge getPointToDrag and getLocalPoints into a single getLocalPoint and use this throughout.
author Chris Cannam
date Thu, 17 Oct 2019 11:12:54 +0100
parents ec837d223bd9
children e95cefd4aa81
comparison
equal deleted inserted replaced
1546:ec837d223bd9 1547:e6362cf5ff1d
237 } else { 237 } else {
238 return false; 238 return false;
239 } 239 }
240 } 240 }
241 241
242 EventVector
243 BoxLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
244 {
245 auto model = ModelById::getAs<BoxModel>(m_model);
246 if (!model) return EventVector();
247
248 sv_frame_t frame = v->getFrameForX(x);
249
250 EventVector local = model->getEventsCovering(frame);
251 if (!local.empty()) return local;
252
253 int fuzz = ViewManager::scalePixelSize(2);
254 sv_frame_t start = v->getFrameForX(x - fuzz);
255 sv_frame_t end = v->getFrameForX(x + fuzz);
256
257 local = model->getEventsStartingWithin(frame, end - frame);
258 if (!local.empty()) return local;
259
260 local = model->getEventsSpanning(start, frame - start);
261 if (!local.empty()) return local;
262
263 return {};
264 }
265
266 bool 242 bool
267 BoxLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, 243 BoxLayer::getLocalPoint(LayerGeometryProvider *v, int x, int y,
268 Event &point) const 244 Event &point) const
269 { 245 {
270 auto model = ModelById::getAs<BoxModel>(m_model); 246 auto model = ModelById::getAs<BoxModel>(m_model);
271 if (!model) return false; 247 if (!model || !model->isReady()) return false;
272 248
273 sv_frame_t frame = v->getFrameForX(x); 249 sv_frame_t frame = v->getFrameForX(x);
274 250
275 EventVector onPoints = model->getEventsCovering(frame); 251 EventVector onPoints = model->getEventsCovering(frame);
276 if (onPoints.empty()) return false; 252 if (onPoints.empty()) return false;
306 if (bestContaining != Event()) { 282 if (bestContaining != Event()) {
307 point = bestContaining; 283 point = bestContaining;
308 } else { 284 } else {
309 int nearestDistance = -1; 285 int nearestDistance = -1;
310 for (const auto &p: onPoints) { 286 for (const auto &p: onPoints) {
287 const auto r = getRange(p);
311 int distance = std::min 288 int distance = std::min
312 (getYForValue(v, p.getValue()) - y, 289 (getYForValue(v, r.first) - y,
313 getYForValue(v, p.getValue() + fabsf(p.getLevel())) - y); 290 getYForValue(v, r.second) - y);
314 if (distance < 0) distance = -distance; 291 if (distance < 0) distance = -distance;
315 if (nearestDistance == -1 || distance < nearestDistance) { 292 if (nearestDistance == -1 || distance < nearestDistance) {
316 nearestDistance = distance; 293 nearestDistance = distance;
317 point = p; 294 point = p;
318 } 295 }
341 318
342 QString 319 QString
343 BoxLayer::getFeatureDescription(LayerGeometryProvider *v, 320 BoxLayer::getFeatureDescription(LayerGeometryProvider *v,
344 QPoint &pos) const 321 QPoint &pos) const
345 { 322 {
346 int x = pos.x();
347
348 auto model = ModelById::getAs<BoxModel>(m_model); 323 auto model = ModelById::getAs<BoxModel>(m_model);
349 if (!model || !model->getSampleRate()) return ""; 324 if (!model || !model->getSampleRate()) return "";
350 325
351 EventVector points = getLocalPoints(v, x); 326 Event box;
352 327
353 if (points.empty()) { 328 if (!getLocalPoint(v, pos.x(), pos.y(), box)) {
354 if (!model->isReady()) { 329 if (!model->isReady()) {
355 return tr("In progress"); 330 return tr("In progress");
356 } else { 331 } else {
357 return tr("No local points"); 332 return tr("No local points");
358 } 333 }
359 } 334 }
360 335
361 Event box;
362 EventVector::iterator i;
363
364 for (i = points.begin(); i != points.end(); ++i) {
365
366 int y0 = getYForValue(v, i->getValue());
367 int y1 = getYForValue(v, i->getValue() + fabsf(i->getLevel()));
368
369 if (pos.y() >= y0 && pos.y() <= y1) {
370 box = *i;
371 break;
372 }
373 }
374
375 if (i == points.end()) return tr("No local points");
376
377 RealTime rt = RealTime::frame2RealTime(box.getFrame(), 336 RealTime rt = RealTime::frame2RealTime(box.getFrame(),
378 model->getSampleRate()); 337 model->getSampleRate());
379 RealTime rd = RealTime::frame2RealTime(box.getDuration(), 338 RealTime rd = RealTime::frame2RealTime(box.getDuration(),
380 model->getSampleRate()); 339 model->getSampleRate());
381 340
382 QString rangeText; 341 QString rangeText;
383 342 auto r = getRange(box);
343
384 rangeText = tr("%1 %2 - %3 %4") 344 rangeText = tr("%1 %2 - %3 %4")
385 .arg(box.getValue()).arg(getScaleUnits()) 345 .arg(r.first).arg(getScaleUnits())
386 .arg(box.getValue() + fabsf(box.getLevel())).arg(getScaleUnits()); 346 .arg(r.second).arg(getScaleUnits());
387 347
388 QString text; 348 QString text;
389 349
390 if (box.getLabel() == "") { 350 if (box.getLabel() == "") {
391 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label")) 351 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label"))
407 367
408 bool 368 bool
409 BoxLayer::snapToFeatureFrame(LayerGeometryProvider *v, 369 BoxLayer::snapToFeatureFrame(LayerGeometryProvider *v,
410 sv_frame_t &frame, 370 sv_frame_t &frame,
411 int &resolution, 371 int &resolution,
412 SnapType snap) const 372 SnapType snap,
373 int ycoord) const
413 { 374 {
414 auto model = ModelById::getAs<BoxModel>(m_model); 375 auto model = ModelById::getAs<BoxModel>(m_model);
415 if (!model) { 376 if (!model) {
416 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 377 return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
417 } 378 }
418 379
419 // SnapLeft / SnapRight: return frame of nearest feature in that 380 // SnapLeft / SnapRight: return frame of nearest feature in that
420 // direction no matter how far away 381 // direction no matter how far away
421 // 382 //
423 // an editing operation, i.e. closest feature in either direction 384 // an editing operation, i.e. closest feature in either direction
424 // but only if it is "close enough" 385 // but only if it is "close enough"
425 386
426 resolution = model->getResolution(); 387 resolution = model->getResolution();
427 388
389 Event containing;
390
391 if (getLocalPoint(v, v->getXForFrame(frame), ycoord, containing)) {
392
393 switch (snap) {
394
395 case SnapLeft:
396 case SnapNeighbouring:
397 frame = containing.getFrame();
398 return true;
399
400 case SnapRight:
401 frame = containing.getFrame() + containing.getDuration();
402 return true;
403 }
404 }
405
428 if (snap == SnapNeighbouring) { 406 if (snap == SnapNeighbouring) {
429 EventVector points = getLocalPoints(v, v->getXForFrame(frame)); 407 return false;
430 if (points.empty()) return false;
431 frame = points.begin()->getFrame();
432 return true;
433 } 408 }
434 409
435 // Normally we snap to the start frame of whichever event we 410 // We aren't actually contained (in time) by any single event, so
436 // find. However here, for SnapRight only, if the end frame of 411 // seek the next one in the relevant direction
437 // whichever event we would have snapped to had we been snapping 412
438 // left is closer than the start frame of the next event to the 413 Event e;
439 // right, then we snap to that frame instead. Clear? 414
440
441 Event left;
442 bool haveLeft = false;
443 if (model->getNearestEventMatching
444 (frame, [](Event) { return true; }, EventSeries::Backward, left)) {
445 haveLeft = true;
446 }
447
448 if (snap == SnapLeft) { 415 if (snap == SnapLeft) {
449 frame = left.getFrame(); 416 if (model->getNearestEventMatching
450 return haveLeft; 417 (frame, [](Event) { return true; }, EventSeries::Backward, e)) {
451 } 418
452 419 if (e.getFrame() + e.getDuration() < frame) {
453 Event right; 420 frame = e.getFrame() + e.getDuration();
454 bool haveRight = false;
455 if (model->getNearestEventMatching
456 (frame, [](Event) { return true; }, EventSeries::Forward, right)) {
457 haveRight = true;
458 }
459
460 if (haveLeft) {
461 sv_frame_t leftEnd = left.getFrame() + left.getDuration();
462 if (leftEnd > frame) {
463 if (haveRight) {
464 if (leftEnd - frame < right.getFrame() - frame) {
465 frame = leftEnd;
466 } else {
467 frame = right.getFrame();
468 }
469 } else { 421 } else {
470 frame = leftEnd; 422 frame = e.getFrame();
471 } 423 }
472 return true; 424 return true;
473 } 425 }
474 } 426 }
475 427
476 if (haveRight) { 428 if (snap == SnapRight) {
477 frame = right.getFrame(); 429 if (model->getNearestEventMatching
478 return true; 430 (frame, [](Event) { return true; }, EventSeries::Forward, e)) {
431
432 frame = e.getFrame();
433 return true;
434 }
479 } 435 }
480 436
481 return false; 437 return false;
482 } 438 }
483 439
605 QPoint localPos; 561 QPoint localPos;
606 Event illuminatePoint(0); 562 Event illuminatePoint(0);
607 bool shouldIlluminate = false; 563 bool shouldIlluminate = false;
608 564
609 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 565 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
610 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), 566 shouldIlluminate = getLocalPoint(v, localPos.x(), localPos.y(),
611 illuminatePoint); 567 illuminatePoint);
612 } 568 }
613 569
614 paint.save(); 570 paint.save();
615 paint.setRenderHint(QPainter::Antialiasing, false); 571 paint.setRenderHint(QPainter::Antialiasing, false);
616 572
618 574
619 for (EventVector::const_iterator i = points.begin(); 575 for (EventVector::const_iterator i = points.begin();
620 i != points.end(); ++i) { 576 i != points.end(); ++i) {
621 577
622 const Event &p(*i); 578 const Event &p(*i);
579 const auto r = getRange(p);
623 580
624 int x = v->getXForFrame(p.getFrame()); 581 int x = v->getXForFrame(p.getFrame());
625 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; 582 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
626 int y = getYForValue(v, p.getValue()); 583 int y = getYForValue(v, r.first);
627 int h = getYForValue(v, p.getValue() + fabsf(p.getLevel())) - y; 584 int h = getYForValue(v, r.second) - y;
628 int ex = x + w; 585 int ex = x + w;
629 int gap = v->scalePixelSize(2); 586 int gap = v->scalePixelSize(2);
630 587
631 EventVector::const_iterator j = i; 588 EventVector::const_iterator j = i;
632 ++j; 589 ++j;
653 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 610 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
654 611
655 if (abs(h) > 2 * fm.height()) { 612 if (abs(h) > 2 * fm.height()) {
656 613
657 QString y0label = QString("%1 %2") 614 QString y0label = QString("%1 %2")
658 .arg(p.getValue()) 615 .arg(r.first)
659 .arg(getScaleUnits()); 616 .arg(getScaleUnits());
660 617
661 QString y1label = QString("%1 %2") 618 QString y1label = QString("%1 %2")
662 .arg(p.getValue() + fabsf(p.getLevel())) 619 .arg(r.second)
663 .arg(getScaleUnits()); 620 .arg(getScaleUnits());
664 621
665 PaintAssistant::drawVisibleText 622 PaintAssistant::drawVisibleText
666 (v, paint, 623 (v, paint,
667 x - fm.width(y0label) - gap, 624 x - fm.width(y0label) - gap,
675 y1label, PaintAssistant::OutlinedText); 632 y1label, PaintAssistant::OutlinedText);
676 633
677 } else { 634 } else {
678 635
679 QString ylabel = QString("%1 %2 - %3 %4") 636 QString ylabel = QString("%1 %2 - %3 %4")
680 .arg(p.getValue()) 637 .arg(r.first)
681 .arg(getScaleUnits()) 638 .arg(getScaleUnits())
682 .arg(p.getValue() + fabsf(p.getLevel())) 639 .arg(r.second)
683 .arg(getScaleUnits()); 640 .arg(getScaleUnits());
684 641
685 PaintAssistant::drawVisibleText 642 PaintAssistant::drawVisibleText
686 (v, paint, 643 (v, paint,
687 x - fm.width(ylabel) - gap, 644 x - fm.width(ylabel) - gap,
878 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) 835 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
879 { 836 {
880 auto model = ModelById::getAs<BoxModel>(m_model); 837 auto model = ModelById::getAs<BoxModel>(m_model);
881 if (!model) return; 838 if (!model) return;
882 839
883 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 840 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) return;
884 841
885 if (m_editingCommand) { 842 if (m_editingCommand) {
886 finish(m_editingCommand); 843 finish(m_editingCommand);
887 m_editingCommand = nullptr; 844 m_editingCommand = nullptr;
888 } 845 }
902 if (!model || !m_editing) return; 859 if (!model || !m_editing) return;
903 860
904 m_editing = false; 861 m_editing = false;
905 862
906 Event p(0); 863 Event p(0);
907 if (!getPointToDrag(v, e->x(), e->y(), p)) return; 864 if (!getLocalPoint(v, e->x(), e->y(), p)) return;
908 if (p.getFrame() != m_editingPoint.getFrame() || 865 if (p.getFrame() != m_editingPoint.getFrame() ||
909 p.getValue() != m_editingPoint.getValue()) return; 866 p.getValue() != m_editingPoint.getValue()) return;
910 867
911 m_editingCommand = new ChangeEventsCommand 868 m_editingCommand = new ChangeEventsCommand
912 (m_model.untyped, tr("Erase Box")); 869 (m_model.untyped, tr("Erase Box"));
922 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) 879 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
923 { 880 {
924 auto model = ModelById::getAs<BoxModel>(m_model); 881 auto model = ModelById::getAs<BoxModel>(m_model);
925 if (!model) return; 882 if (!model) return;
926 883
927 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) { 884 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) {
928 return; 885 return;
929 } 886 }
930 887
931 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); 888 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
932 m_dragPointY = getYForValue(v, m_editingPoint.getValue()); 889 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
1006 { 963 {
1007 auto model = ModelById::getAs<BoxModel>(m_model); 964 auto model = ModelById::getAs<BoxModel>(m_model);
1008 if (!model) return false; 965 if (!model) return false;
1009 966
1010 Event region(0); 967 Event region(0);
1011 if (!getPointToDrag(v, e->x(), e->y(), region)) return false; 968 if (!getLocalPoint(v, e->x(), e->y(), region)) return false;
1012 969
1013 ItemEditDialog::LabelOptions labelOptions; 970 ItemEditDialog::LabelOptions labelOptions;
1014 labelOptions.valueLabel = tr("Minimum Value"); 971 labelOptions.valueLabel = tr("Minimum Value");
1015 labelOptions.levelLabel = tr("Value Extent"); 972 labelOptions.levelLabel = tr("Value Extent");
1016 labelOptions.valueUnits = getScaleUnits(); 973 labelOptions.valueUnits = getScaleUnits();