1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2020 MuseScore BVBA and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19
20 #include "avsomr.h"
21
22 #include "avslog.h"
23
24 namespace {
25 static int TRASH_GLYPH_SIZE{10};
26 static int DEFAULT_SYSTEM_GAP{200};
27 static float SYSTEM_GAP_PERCENT{0.55f};
28 }
29
30 using namespace Ms::Avs;
31
AvsOmr()32 AvsOmr::AvsOmr()
33 {
34
35 }
36
37 //---------------------------------------------------------
38 // resolve - calculates some elements and attributes
39 //---------------------------------------------------------
40
resolve()41 void AvsOmr::resolve()
42 {
43 auto firstMS = [](Sheet* sh) -> const MStack* {
44 if (sh->page.systems.empty())
45 return nullptr;
46
47 for (int si = 0; si < sh->page.systems.count(); ++si) {
48 if (sh->page.systems.at(si).mstacks.empty())
49 continue;
50
51 return &sh->page.systems.at(si).mstacks.first();
52 }
53 return nullptr;
54 };
55
56 auto lastMS = [](Sheet* sh) -> const MStack* {
57 if (sh->page.systems.empty())
58 return nullptr;
59
60 for (int si = (sh->page.systems.count() - 1); si != 0; --si) {
61 if (sh->page.systems.at(si).mstacks.empty()) {
62 continue;
63 }
64 return &sh->page.systems.at(si).mstacks.last();
65 }
66 return nullptr;
67 };
68
69 auto staffBarlineBBox = [](const Sheet* sh, const System& sys, const Staff& staff) {
70
71 IF_FAILED(!staff.barlines.empty()) {
72 return QRect();
73 }
74
75 ID barID = staff.barlines.first();
76 Barline bar = sys.inters.barlines.value(barID);
77 const Glyph* gly = sh->glyphs.value(bar.glyphID, nullptr);
78 IF_FAILED(gly) {
79 return QRect();
80 }
81
82 return gly->bbox;
83 };
84
85 auto isTrashGlyph = [](const AvsOmr::Glyph* g) {
86 if (g->bbox.width() <= TRASH_GLYPH_SIZE && g->bbox.height() <= TRASH_GLYPH_SIZE)
87 return true;
88
89 return false;
90 };
91
92 Idx midx{0};
93 for (int shi = 0; shi < _sheets.count(); ++shi) {
94
95 Sheet* sh = _sheets[shi];
96 if (sh->page.systems.empty())
97 continue;
98
99 for (System& sys : sh->page.systems) {
100 // meausure idx
101 for (MStack& ms : sys.mstacks) {
102 ms.idx = midx;
103 ++midx;
104 }
105
106 // system top and bottom
107 IF_FAILED(!sys.part.staffs.empty()) {
108 continue;
109 }
110
111 QRect topBarBBox = staffBarlineBBox(sh, sys, sys.part.staffs.first());
112 QRect bottomBarBBox = staffBarlineBBox(sh, sys, sys.part.staffs.last());
113 sys.top = topBarBBox.top();
114 sys.bottom = bottomBarBBox.bottom() + 40;
115 }
116
117 // meausure ranges
118 const MStack* fms = firstMS(sh);
119 const MStack* lms = lastMS(sh);
120 sh->mbeginIdx = fms ? fms->idx : 0;
121 sh->mendIdx = lms ? lms->idx : 0;
122
123 // glyph used
124 QList<QRect> usedBBoxs;
125 int gap = 5;
126 for (const Glyph* g : sh->glyphs) {
127 if (GlyphUsed::Used == g->used)
128 usedBBoxs.append(g->bbox.adjusted(-gap, -gap, gap, gap));
129 }
130
131 auto isUsedContains = [](const QList<QRect>& usedBBoxs, const QRect& bbox) {
132 for (const QRect& r : usedBBoxs) {
133 if (r.contains(bbox))
134 return true;
135 }
136 return false;
137 };
138
139 //! TODO optimization may be needed because complexity O^2
140 for (Glyph* g : sh->glyphs) {
141 if (isTrashGlyph(g)) {
142 g->used = GlyphUsed::Trash;
143 }
144 else if (GlyphUsed::Free == g->used) {
145 if (isUsedContains(usedBBoxs, g->bbox)) {
146 g->used = GlyphUsed::Free_Covered;
147 }
148 }
149 }
150 }
151 }
152
153 //---------------------------------------------------------
154 // sheetNumByMeausereIdx
155 //---------------------------------------------------------
156
sheetNumByMeausereIdx(const Idx & meausureIdx) const157 AvsOmr::Num AvsOmr::sheetNumByMeausereIdx(const Idx& meausureIdx) const
158 {
159 for (const Sheet* sh : _sheets)
160 if (meausureIdx >= sh->mbeginIdx && meausureIdx <= sh->mendIdx)
161 return sh->num;
162
163 return 0;
164 }
165
166 //---------------------------------------------------------
167 // sheet
168 //---------------------------------------------------------
169
sheet(const Num & sheetNum) const170 const AvsOmr::Sheet* AvsOmr::sheet(const Num& sheetNum) const
171 {
172 for (const Sheet* sh : _sheets)
173 if (sheetNum == sh->num)
174 return sh;
175
176 return nullptr;
177 }
178
179 //---------------------------------------------------------
180 // isGlyphUsed
181 //---------------------------------------------------------
182
isGlyphUsed(const ID & glypthID) const183 bool AvsOmr::Sheet::isGlyphUsed(const ID& glypthID) const
184 {
185 for (const System& sys : page.systems)
186 if (sys.inters.usedglyphs.contains(glypthID))
187 return true;
188
189 return false;
190 }
191
192 //---------------------------------------------------------
193 // isGlyphFree
194 //---------------------------------------------------------
195
isGlyphFree(const ID & glypthID) const196 bool AvsOmr::Sheet::isGlyphFree(const ID& glypthID) const
197 {
198 for (const System& sys : page.systems)
199 if (sys.freeglyphs.contains(glypthID))
200 return true;
201
202 return false;
203 }
204
205 //---------------------------------------------------------
206 // stackByIdx
207 //---------------------------------------------------------
208
stackByIdx(Idx idx,Idx * idxInSys) const209 const AvsOmr::MStack& AvsOmr::System::stackByIdx(Idx idx, Idx *idxInSys) const
210 {
211 for (int i = 0; i < mstacks.count(); ++i) {
212 const MStack& m = mstacks.at(i);
213 if (m.idx == idx) {
214 if (idxInSys)
215 *idxInSys = i;
216
217 return m;
218 }
219 }
220
221 static MStack dummy;
222 return dummy;
223 }
224
225 //---------------------------------------------------------
226 // mmetrics
227 //---------------------------------------------------------
228
mmetrics(const Num & sheetNum,const Idx & meausureIdx) const229 AvsOmr::MMetrics AvsOmr::mmetrics(const Num &sheetNum, const Idx &meausureIdx) const
230 {
231 const Sheet* sh = sheet(sheetNum);
232 IF_ASSERT(sh) {
233 return MMetrics();
234 }
235
236 MMetrics mm;
237 int sysCount = sh->page.systems.count();
238 for (int si = 0; si < sysCount; ++si) {
239 const System& sys = sh->page.systems.at(si);
240
241 Idx idxInSys{0};
242 const AvsOmr::MStack& m = sys.stackByIdx(meausureIdx, &idxInSys);
243 if (!m.isValid())
244 continue;
245
246 // bbox
247 mm.bbox.setLeft(m.left);
248 mm.bbox.setRight(m.right);
249 mm.bbox.setTop(sys.top);
250 mm.bbox.setBottom(sys.bottom);
251
252 // bbox header
253 if (0 == idxInSys) {
254 if (!sys.part.staffs.empty()) {
255 const Staff& topStaff = sys.part.staffs.first();
256 mm.hbbox.setLeft(topStaff.header.start);
257 mm.hbbox.setRight(topStaff.header.stop);
258 mm.hbbox.setTop(mm.bbox.top());
259 mm.hbbox.setBottom(mm.bbox.bottom());
260 }
261 }
262
263 // bbox elements
264 mm.ebbox = mm.bbox;
265
266 bool isFirstSys = (0 == si);
267 bool isLastSys = ((sysCount - 1) == si);
268 int halfH = mm.bbox.height() / 2;
269
270 if (1 == sysCount) {
271 mm.ebbox.setTop(mm.ebbox.top() - halfH);
272 mm.ebbox.setBottom(mm.ebbox.bottom() + halfH);
273 }
274 else {
275
276 auto gapToNextSys = [sh](const System& sys, size_t si) {
277 const System& nextSys = sh->page.systems.at(si + 1);
278 int gapSys = nextSys.top - sys.bottom;
279 IF_ASSERT(gapSys > 0) {
280 gapSys = DEFAULT_SYSTEM_GAP;
281 }
282 return gapSys;
283 };
284
285 auto gapToPrevSys = [sh](const System& sys, size_t si) {
286 const System& prevSys = sh->page.systems.at(si - 1);
287 int gapSys = sys.top - prevSys.bottom;
288 IF_ASSERT(gapSys > 0) {
289 gapSys = DEFAULT_SYSTEM_GAP;
290 }
291 return gapSys;
292 };
293
294 if (isFirstSys) {
295 mm.ebbox.setTop(mm.ebbox.top() - halfH);
296 int gapSys = gapToNextSys(sys, si);
297 mm.ebbox.setBottom(mm.ebbox.bottom() + (gapSys * SYSTEM_GAP_PERCENT));
298 }
299 else if (isLastSys) {
300 int gapSys = gapToPrevSys(sys, si);
301 mm.ebbox.setTop(mm.ebbox.top() - (gapSys * SYSTEM_GAP_PERCENT));
302 mm.ebbox.setBottom(mm.ebbox.bottom() + halfH);
303 }
304 else {
305 int gapPrevSys = gapToPrevSys(sys, si);
306 mm.ebbox.setTop(mm.ebbox.top() - (gapPrevSys * SYSTEM_GAP_PERCENT));
307
308 int gapNextSys = gapToNextSys(sys, si);
309 mm.ebbox.setBottom(mm.ebbox.bottom() + (gapNextSys * SYSTEM_GAP_PERCENT));
310 }
311 }
312
313 //! RETURN
314 return mm;
315 }
316
317 return mm;
318 }
319
320 //---------------------------------------------------------
321 // glyphsByBBox
322 //---------------------------------------------------------
323
glyphsByBBox(const Num & sheetNum,const QRect & bbox,QList<GlyphUsed> & accepted) const324 QList<const AvsOmr::Glyph*> AvsOmr::glyphsByBBox(const Num& sheetNum, const QRect& bbox, QList<GlyphUsed>& accepted) const
325 {
326 const Sheet* sh = sheet(sheetNum);
327 IF_ASSERT(sh) {
328 return QList<const AvsOmr::Glyph*>();
329 }
330
331 QList<const Glyph*> list;
332 for (auto g : sh->glyphs) {
333 if (!accepted.contains(g->used))
334 continue;
335
336 if (bbox.contains(g->bbox))
337 list.append(g);
338 }
339
340 return list;
341 }
342
343 //---------------------------------------------------------
344 // config
345 //---------------------------------------------------------
346
config()347 AvsOmr::Config& AvsOmr::config()
348 {
349 return _config;
350 }
351
352 //---------------------------------------------------------
353 // config const
354 //---------------------------------------------------------
355
config() const356 const AvsOmr::Config& AvsOmr::config() const
357 {
358 return _config;
359 }
360
361 //---------------------------------------------------------
362 // setMsmrFile
363 //---------------------------------------------------------
364
setMsmrFile(std::shared_ptr<MsmrFile> file)365 void AvsOmr::setMsmrFile(std::shared_ptr<MsmrFile> file)
366 {
367 _msmrFile = file;
368 }
369
370 //---------------------------------------------------------
371 // msmrFile
372 //---------------------------------------------------------
373
msmrFile() const374 std::shared_ptr<MsmrFile> AvsOmr::msmrFile() const
375 {
376 return _msmrFile;
377 }
378
379 //---------------------------------------------------------
380 // info
381 //---------------------------------------------------------
382
info() const383 const AvsOmr::Info& AvsOmr::info() const
384 {
385 return _info;
386 }
387