MuseScore/avsomr/avsomr.cpp
2020-05-28 09:50:45 +02:00

398 lines
12 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "avsomr.h"
#include "avslog.h"
namespace {
static int TRASH_GLYPH_SIZE{ 10 };
static int DEFAULT_SYSTEM_GAP{ 200 };
static float SYSTEM_GAP_PERCENT{ 0.55f };
}
using namespace Ms::Avs;
AvsOmr::AvsOmr()
{
}
//---------------------------------------------------------
// resolve - calculates some elements and attributes
//---------------------------------------------------------
void AvsOmr::resolve()
{
auto firstMS = [](Sheet* sh) -> const MStack* {
if (sh->page.systems.empty()) {
return nullptr;
}
for (int si = 0; si < sh->page.systems.count(); ++si) {
if (sh->page.systems.at(si).mstacks.empty()) {
continue;
}
return &sh->page.systems.at(si).mstacks.first();
}
return nullptr;
};
auto lastMS = [](Sheet* sh) -> const MStack* {
if (sh->page.systems.empty()) {
return nullptr;
}
for (int si = (sh->page.systems.count() - 1); si != 0; --si) {
if (sh->page.systems.at(si).mstacks.empty()) {
continue;
}
return &sh->page.systems.at(si).mstacks.last();
}
return nullptr;
};
auto staffBarlineBBox = [](const Sheet* sh, const System& sys, const Staff& staff) {
IF_FAILED(!staff.barlines.empty()) {
return QRect();
}
ID barID = staff.barlines.first();
Barline bar = sys.inters.barlines.value(barID);
const Glyph* gly = sh->glyphs.value(bar.glyphID, nullptr);
IF_FAILED(gly) {
return QRect();
}
return gly->bbox;
};
auto isTrashGlyph = [](const AvsOmr::Glyph* g) {
if (g->bbox.width() <= TRASH_GLYPH_SIZE && g->bbox.height() <= TRASH_GLYPH_SIZE) {
return true;
}
return false;
};
Idx midx{ 0 };
for (int shi = 0; shi < _sheets.count(); ++shi) {
Sheet* sh = _sheets[shi];
if (sh->page.systems.empty()) {
continue;
}
for (System& sys : sh->page.systems) {
// meausure idx
for (MStack& ms : sys.mstacks) {
ms.idx = midx;
++midx;
}
// system top and bottom
IF_FAILED(!sys.part.staffs.empty()) {
continue;
}
QRect topBarBBox = staffBarlineBBox(sh, sys, sys.part.staffs.first());
QRect bottomBarBBox = staffBarlineBBox(sh, sys, sys.part.staffs.last());
sys.top = topBarBBox.top();
sys.bottom = bottomBarBBox.bottom() + 40;
}
// meausure ranges
const MStack* fms = firstMS(sh);
const MStack* lms = lastMS(sh);
sh->mbeginIdx = fms ? fms->idx : 0;
sh->mendIdx = lms ? lms->idx : 0;
// glyph used
QList<QRect> usedBBoxs;
int gap = 5;
for (const Glyph* g : sh->glyphs) {
if (GlyphUsed::Used == g->used) {
usedBBoxs.append(g->bbox.adjusted(-gap, -gap, gap, gap));
}
}
auto isUsedContains = [](const QList<QRect>& usedBBoxs, const QRect& bbox) {
for (const QRect& r : usedBBoxs) {
if (r.contains(bbox)) {
return true;
}
}
return false;
};
//! TODO optimization may be needed because complexity O^2
for (Glyph* g : sh->glyphs) {
if (isTrashGlyph(g)) {
g->used = GlyphUsed::Trash;
} else if (GlyphUsed::Free == g->used) {
if (isUsedContains(usedBBoxs, g->bbox)) {
g->used = GlyphUsed::Free_Covered;
}
}
}
}
}
//---------------------------------------------------------
// sheetNumByMeausereIdx
//---------------------------------------------------------
AvsOmr::Num AvsOmr::sheetNumByMeausereIdx(const Idx& meausureIdx) const
{
for (const Sheet* sh : _sheets) {
if (meausureIdx >= sh->mbeginIdx && meausureIdx <= sh->mendIdx) {
return sh->num;
}
}
return 0;
}
//---------------------------------------------------------
// sheet
//---------------------------------------------------------
const AvsOmr::Sheet* AvsOmr::sheet(const Num& sheetNum) const
{
for (const Sheet* sh : _sheets) {
if (sheetNum == sh->num) {
return sh;
}
}
return nullptr;
}
//---------------------------------------------------------
// isGlyphUsed
//---------------------------------------------------------
bool AvsOmr::Sheet::isGlyphUsed(const ID& glypthID) const
{
for (const System& sys : page.systems) {
if (sys.inters.usedglyphs.contains(glypthID)) {
return true;
}
}
return false;
}
//---------------------------------------------------------
// isGlyphFree
//---------------------------------------------------------
bool AvsOmr::Sheet::isGlyphFree(const ID& glypthID) const
{
for (const System& sys : page.systems) {
if (sys.freeglyphs.contains(glypthID)) {
return true;
}
}
return false;
}
//---------------------------------------------------------
// stackByIdx
//---------------------------------------------------------
const AvsOmr::MStack& AvsOmr::System::stackByIdx(Idx idx, Idx* idxInSys) const
{
for (int i = 0; i < mstacks.count(); ++i) {
const MStack& m = mstacks.at(i);
if (m.idx == idx) {
if (idxInSys) {
*idxInSys = i;
}
return m;
}
}
static MStack dummy;
return dummy;
}
//---------------------------------------------------------
// mmetrics
//---------------------------------------------------------
AvsOmr::MMetrics AvsOmr::mmetrics(const Num& sheetNum, const Idx& meausureIdx) const
{
const Sheet* sh = sheet(sheetNum);
IF_ASSERT(sh) {
return MMetrics();
}
MMetrics mm;
int sysCount = sh->page.systems.count();
for (int si = 0; si < sysCount; ++si) {
const System& sys = sh->page.systems.at(si);
Idx idxInSys{ 0 };
const AvsOmr::MStack& m = sys.stackByIdx(meausureIdx, &idxInSys);
if (!m.isValid()) {
continue;
}
// bbox
mm.bbox.setLeft(m.left);
mm.bbox.setRight(m.right);
mm.bbox.setTop(sys.top);
mm.bbox.setBottom(sys.bottom);
// bbox header
if (0 == idxInSys) {
if (!sys.part.staffs.empty()) {
const Staff& topStaff = sys.part.staffs.first();
mm.hbbox.setLeft(topStaff.header.start);
mm.hbbox.setRight(topStaff.header.stop);
mm.hbbox.setTop(mm.bbox.top());
mm.hbbox.setBottom(mm.bbox.bottom());
}
}
// bbox elements
mm.ebbox = mm.bbox;
bool isFirstSys = (0 == si);
bool isLastSys = ((sysCount - 1) == si);
int halfH = mm.bbox.height() / 2;
if (1 == sysCount) {
mm.ebbox.setTop(mm.ebbox.top() - halfH);
mm.ebbox.setBottom(mm.ebbox.bottom() + halfH);
} else {
auto gapToNextSys = [sh](const System& sys, size_t si) {
const System& nextSys = sh->page.systems.at(si + 1);
int gapSys = nextSys.top - sys.bottom;
IF_ASSERT(gapSys > 0) {
gapSys = DEFAULT_SYSTEM_GAP;
}
return gapSys;
};
auto gapToPrevSys = [sh](const System& sys, size_t si) {
const System& prevSys = sh->page.systems.at(si - 1);
int gapSys = sys.top - prevSys.bottom;
IF_ASSERT(gapSys > 0) {
gapSys = DEFAULT_SYSTEM_GAP;
}
return gapSys;
};
if (isFirstSys) {
mm.ebbox.setTop(mm.ebbox.top() - halfH);
int gapSys = gapToNextSys(sys, si);
mm.ebbox.setBottom(mm.ebbox.bottom() + (gapSys * SYSTEM_GAP_PERCENT));
} else if (isLastSys) {
int gapSys = gapToPrevSys(sys, si);
mm.ebbox.setTop(mm.ebbox.top() - (gapSys * SYSTEM_GAP_PERCENT));
mm.ebbox.setBottom(mm.ebbox.bottom() + halfH);
} else {
int gapPrevSys = gapToPrevSys(sys, si);
mm.ebbox.setTop(mm.ebbox.top() - (gapPrevSys * SYSTEM_GAP_PERCENT));
int gapNextSys = gapToNextSys(sys, si);
mm.ebbox.setBottom(mm.ebbox.bottom() + (gapNextSys * SYSTEM_GAP_PERCENT));
}
}
//! RETURN
return mm;
}
return mm;
}
//---------------------------------------------------------
// glyphsByBBox
//---------------------------------------------------------
QList<const AvsOmr::Glyph*> AvsOmr::glyphsByBBox(const Num& sheetNum, const QRect& bbox,
QList<GlyphUsed>& accepted) const
{
const Sheet* sh = sheet(sheetNum);
IF_ASSERT(sh) {
return QList<const AvsOmr::Glyph*>();
}
QList<const Glyph*> list;
for (auto g : sh->glyphs) {
if (!accepted.contains(g->used)) {
continue;
}
if (bbox.contains(g->bbox)) {
list.append(g);
}
}
return list;
}
//---------------------------------------------------------
// config
//---------------------------------------------------------
AvsOmr::Config& AvsOmr::config()
{
return _config;
}
//---------------------------------------------------------
// config const
//---------------------------------------------------------
const AvsOmr::Config& AvsOmr::config() const
{
return _config;
}
//---------------------------------------------------------
// setMsmrFile
//---------------------------------------------------------
void AvsOmr::setMsmrFile(std::shared_ptr<MsmrFile> file)
{
_msmrFile = file;
}
//---------------------------------------------------------
// msmrFile
//---------------------------------------------------------
std::shared_ptr<MsmrFile> AvsOmr::msmrFile() const
{
return _msmrFile;
}
//---------------------------------------------------------
// info
//---------------------------------------------------------
const AvsOmr::Info& AvsOmr::info() const
{
return _info;
}