Mercurial > hg > svgui
view view/AlignmentView.cpp @ 1619:36634b427d61
Fix wrongly-written test which made the mapping alignments line up wrongly in some cases where adjacent panes were related (but, because of this test, the alignment view thought they were not)
author | Chris Cannam |
---|---|
date | Tue, 18 Aug 2020 14:49:36 +0100 |
parents | 911330a28a7c |
children | dc0e47f234a2 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006-2014 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "AlignmentView.h" #include <QPainter> #include "data/model/SparseOneDimensionalModel.h" #include "layer/TimeInstantLayer.h" //#define DEBUG_ALIGNMENT_VIEW 1 using std::vector; using std::set; AlignmentView::AlignmentView(QWidget *w) : View(w, false), m_above(nullptr), m_below(nullptr), m_reference(nullptr), m_leftmostAbove(-1), m_rightmostAbove(-1) { setObjectName(tr("AlignmentView")); } void AlignmentView::keyFramesChanged() { #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView " << getId() << "::keyFramesChanged" << endl; #endif // This is just a notification that we need to rebuild - so all we // do here is clear, and rebuild on demand later QMutexLocker locker(&m_mapsMutex); m_fromAboveMap.clear(); m_fromReferenceMap.clear(); } void AlignmentView::globalCentreFrameChanged(sv_frame_t f) { View::globalCentreFrameChanged(f); update(); } void AlignmentView::viewCentreFrameChanged(View *v, sv_frame_t f) { View::viewCentreFrameChanged(v, f); if (v == m_above) { m_centreFrame = f; update(); } else if (v == m_below) { update(); } } void AlignmentView::viewManagerPlaybackFrameChanged(sv_frame_t) { update(); } void AlignmentView::viewAboveZoomLevelChanged(ZoomLevel level, bool) { m_zoomLevel = level; update(); } void AlignmentView::viewBelowZoomLevelChanged(ZoomLevel, bool) { update(); } void AlignmentView::setAboveView(View *v) { if (m_above) { disconnect(m_above, nullptr, this, nullptr); } m_above = v; if (m_above) { connect(m_above, SIGNAL(zoomLevelChanged(ZoomLevel, bool)), this, SLOT(viewAboveZoomLevelChanged(ZoomLevel, bool))); connect(m_above, SIGNAL(propertyContainerAdded(PropertyContainer *)), this, SLOT(keyFramesChanged())); connect(m_above, SIGNAL(layerModelChanged()), this, SLOT(keyFramesChanged())); } keyFramesChanged(); } void AlignmentView::setBelowView(View *v) { if (m_below) { disconnect(m_below, nullptr, this, nullptr); } m_below = v; if (m_below) { connect(m_below, SIGNAL(zoomLevelChanged(ZoomLevel, bool)), this, SLOT(viewBelowZoomLevelChanged(ZoomLevel, bool))); connect(m_below, SIGNAL(propertyContainerAdded(PropertyContainer *)), this, SLOT(keyFramesChanged())); connect(m_below, SIGNAL(layerModelChanged()), this, SLOT(keyFramesChanged())); } keyFramesChanged(); } void AlignmentView::setReferenceView(View *view) { m_reference = view; } void AlignmentView::paintEvent(QPaintEvent *) { if (m_above == nullptr || m_below == nullptr || !m_manager) return; #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView " << getId() << "::paintEvent" << endl; #endif bool darkPalette = false; if (m_manager) darkPalette = m_manager->getGlobalDarkBackground(); QColor fg, bg; if (darkPalette) { fg = Qt::gray; bg = Qt::black; } else { fg = Qt::black; bg = Qt::gray; } QPainter paint(this); paint.setPen(QPen(fg, 2)); paint.setBrush(Qt::NoBrush); paint.setRenderHint(QPainter::Antialiasing, true); paint.fillRect(rect(), bg); QMutexLocker locker(&m_mapsMutex); if (m_fromAboveMap.empty()) { reconnectModels(); buildMaps(); } #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView " << getId() << "::paintEvent: painting " << m_fromAboveMap.size() << " mappings" << endl; #endif int w = width(); int h = height(); if (m_leftmostAbove >= 0) { #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove << ", we have a relationship with the pane above us: showing " << "mappings in relation to that" << endl; #endif for (const auto &km: m_fromAboveMap) { sv_frame_t af = km.first; sv_frame_t bf = km.second; if (af < m_leftmostAbove || af > m_rightmostAbove) { continue; } int ax = m_above->getXForFrame(af); int bx = m_below->getXForFrame(bf); if (ax >= 0 || ax < w || bx >= 0 || bx < w) { paint.drawLine(ax, 0, bx, h); } } } else if (m_reference != nullptr) { // the below has nothing in common with the above: show things // in common with the reference instead #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove << ", we have no relationship with the pane above us: showing " << "mappings in relation to the reference instead" << endl; #endif for (const auto &km: m_fromReferenceMap) { sv_frame_t af = km.first; sv_frame_t bf = km.second; int ax = m_reference->getXForFrame(af); int bx = m_below->getXForFrame(bf); if (ax >= 0 || ax < w || bx >= 0 || bx < w) { paint.drawLine(ax, 0, bx, h); } } } paint.end(); } void AlignmentView::reconnectModels() { vector<ModelId> toConnect { getSalientModel(m_above), getSalientModel(m_below) }; for (auto modelId: toConnect) { if (auto model = ModelById::get(modelId)) { auto referenceId = model->getAlignmentReference(); if (!referenceId.isNone()) { toConnect.push_back(referenceId); } } } for (auto modelId: toConnect) { if (auto model = ModelById::get(modelId)) { auto ptr = model.get(); disconnect(ptr, 0, this, 0); connect(ptr, SIGNAL(modelChanged(ModelId)), this, SLOT(keyFramesChanged())); connect(ptr, SIGNAL(completionChanged(ModelId)), this, SLOT(keyFramesChanged())); connect(ptr, SIGNAL(alignmentCompletionChanged(ModelId)), this, SLOT(keyFramesChanged())); } } } void AlignmentView::buildMaps() { #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView " << getId() << "::buildMaps" << endl; #endif sv_frame_t resolution = 1; set<sv_frame_t> keyFramesBelow; for (auto f: getKeyFrames(m_below, resolution)) { keyFramesBelow.insert(f); } foreach(sv_frame_t f, keyFramesBelow) { sv_frame_t rf = m_below->alignToReference(f); m_fromReferenceMap.insert({ rf, f }); } vector<sv_frame_t> keyFrames = getKeyFrames(m_above, resolution); // These are the most extreme leftward and rightward frames in // "above" that have distinct corresponding frames in // "below". Anything left of m_leftmostAbove or right of // m_rightmostAbove maps effectively off one end or the other of // the below view. (They don't actually map off the ends, they // just all map to the same first/last destination frame. But we // don't want to display their mappings, as they're just noise.) m_leftmostAbove = -1; m_rightmostAbove = -1; sv_frame_t prevRf = -1; sv_frame_t prevBf = -1; foreach (sv_frame_t f, keyFrames) { sv_frame_t rf = m_above->alignToReference(f); sv_frame_t bf = m_below->alignFromReference(rf); if (prevBf > 0 && bf > prevBf) { if (m_leftmostAbove < 0 && prevBf > 0 && bf > prevBf) { m_leftmostAbove = prevRf; } m_rightmostAbove = rf; } prevRf = rf; prevBf = bf; bool mappedSomething = false; if (resolution > 1) { if (keyFramesBelow.find(bf) == keyFramesBelow.end()) { sv_frame_t f1 = f + resolution; sv_frame_t rf1 = m_above->alignToReference(f1); sv_frame_t bf1 = m_below->alignFromReference(rf1); for (sv_frame_t probe = bf + 1; probe <= bf1; ++probe) { if (keyFramesBelow.find(probe) != keyFramesBelow.end()) { m_fromAboveMap.insert({ f, probe }); mappedSomething = true; } } } } if (!mappedSomething) { m_fromAboveMap.insert({ f, bf }); } } #ifdef DEBUG_ALIGNMENT_VIEW SVCERR << "AlignmentView " << getId() << "::buildMaps: have " << m_fromAboveMap.size() << " mappings" << endl; #endif } vector<sv_frame_t> AlignmentView::getKeyFrames(View *view, sv_frame_t &resolution) { resolution = 1; if (!view) { return getDefaultKeyFrames(); } ModelId m = getSalientModel(view); auto model = ModelById::getAs<SparseOneDimensionalModel>(m); if (!model) { return getDefaultKeyFrames(); } resolution = model->getResolution(); vector<sv_frame_t> keyFrames; EventVector pp = model->getAllEvents(); for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) { keyFrames.push_back(pi->getFrame()); } return keyFrames; } vector<sv_frame_t> AlignmentView::getDefaultKeyFrames() { vector<sv_frame_t> keyFrames; return keyFrames; #ifdef NOT_REALLY if (!m_above || !m_manager) return keyFrames; sv_samplerate_t rate = m_manager->getMainModelSampleRate(); if (rate == 0) return keyFrames; for (sv_frame_t f = m_above->getModelsStartFrame(); f <= m_above->getModelsEndFrame(); f += sv_frame_t(rate * 5 + 0.5)) { keyFrames.push_back(f); } return keyFrames; #endif } ModelId AlignmentView::getSalientModel(View *view) { ModelId m; // get the topmost such for (int i = 0; i < view->getLayerCount(); ++i) { if (qobject_cast<TimeInstantLayer *>(view->getLayer(i))) { ModelId mm = view->getLayer(i)->getModel(); if (ModelById::isa<SparseOneDimensionalModel>(mm)) { m = mm; } } } return m; }