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