Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "spannermap.h"
24 : #include "spanner.h"
25 : #include "part.h"
26 :
27 : #include "log.h"
28 :
29 : using namespace mu;
30 :
31 : namespace mu::engraving {
32 : //---------------------------------------------------------
33 : // SpannerMap
34 : //---------------------------------------------------------
35 :
36 2334 : SpannerMap::SpannerMap()
37 2334 : : std::multimap<int, Spanner*>()
38 : {
39 2334 : m_dirty = true;
40 2334 : }
41 :
42 : //---------------------------------------------------------
43 : // update
44 : // updates the internal lookup tree, not the map itself
45 : //---------------------------------------------------------
46 :
47 2789 : void SpannerMap::update() const
48 : {
49 2804 : TRACEFUNC;
50 2789 : IntervalList regularIntervals;
51 2789 : IntervalList collisionFreeIntervals;
52 :
53 2789 : collectIntervals(regularIntervals, collisionFreeIntervals);
54 :
55 2789 : m_tree = interval_tree::IntervalTree<Spanner*>(std::move(regularIntervals));
56 2789 : m_collisionFreeTree = interval_tree::IntervalTree<Spanner*>(std::move(collisionFreeIntervals));
57 2789 : m_dirty = false;
58 2789 : }
59 :
60 : //---------------------------------------------------------
61 : // findContained
62 : //---------------------------------------------------------
63 :
64 180 : const SpannerMap::IntervalList& SpannerMap::findContained(int start, int stop, bool excludeCollisions) const
65 : {
66 180 : if (m_dirty) {
67 11 : update();
68 : }
69 :
70 180 : m_results.clear();
71 :
72 180 : if (excludeCollisions) {
73 0 : m_results = m_collisionFreeTree.findContained(start, stop);
74 : } else {
75 180 : m_results = m_tree.findContained(start, stop);
76 : }
77 :
78 180 : return m_results;
79 : }
80 :
81 : //---------------------------------------------------------
82 : // findOverlapping
83 : //---------------------------------------------------------
84 :
85 143891 : const SpannerMap::IntervalList& SpannerMap::findOverlapping(int start, int stop, bool excludeCollisions) const
86 : {
87 143891 : if (m_dirty) {
88 2778 : update();
89 : }
90 :
91 143891 : m_results.clear();
92 :
93 143891 : if (excludeCollisions) {
94 43808 : m_results = m_collisionFreeTree.findOverlapping(start, stop);
95 : } else {
96 100083 : m_results = m_tree.findOverlapping(start, stop);
97 : }
98 :
99 143891 : return m_results;
100 : }
101 :
102 2789 : void SpannerMap::collectIntervals(IntervalList& regularIntervals, IntervalList& collisionFreeIntervals) const
103 : {
104 : using IntervalsByType = std::map<ElementType, IntervalList>;
105 : using IntervalsByPart = std::map<ID, IntervalsByType>;
106 :
107 2789 : IntervalsByPart intervalsByPart;
108 :
109 : //!Note Because of the current UX of spanners adjustments spanners collision is a regular thing,
110 : //! so we have to manage those cases when two similar spanners (e.g. Pedal line) are overlapping
111 : //! with each other.
112 2789 : constexpr int collidingSpannersPadding = 1;
113 :
114 6443 : for (const auto& pair : *this) {
115 3654 : Spanner* spanner = pair.second;
116 :
117 3654 : int newSpannerStartTick = spanner->tick().ticks();
118 3654 : int newSpannerEndTick = spanner->tick2().ticks();
119 :
120 3654 : IntervalsByType& intervalsByType = intervalsByPart[spanner->part()->id()];
121 3654 : IntervalList& intervalList = intervalsByType[spanner->type()];
122 :
123 3654 : if (!intervalList.empty()) {
124 2445 : auto lastIntervalIt = intervalList.rbegin();
125 2445 : if (lastIntervalIt->stop >= newSpannerStartTick) {
126 490 : if (!lastIntervalIt->value->isLinked(spanner)) {
127 448 : lastIntervalIt->stop = newSpannerStartTick - collidingSpannersPadding;
128 : }
129 : }
130 : }
131 :
132 3654 : intervalList.emplace_back(interval_tree::Interval<Spanner*>(newSpannerStartTick,
133 : newSpannerEndTick,
134 3654 : spanner));
135 :
136 3654 : regularIntervals.push_back(interval_tree::Interval(newSpannerStartTick,
137 : newSpannerEndTick,
138 3654 : spanner));
139 : }
140 :
141 3641 : for (const auto& pair : intervalsByPart) {
142 2061 : for (const auto& intervals : pair.second) {
143 1209 : collisionFreeIntervals.insert(collisionFreeIntervals.end(),
144 : intervals.second.begin(),
145 : intervals.second.end());
146 : }
147 : }
148 2789 : }
149 :
150 : //---------------------------------------------------------
151 : // addSpanner
152 : //---------------------------------------------------------
153 :
154 1513 : void SpannerMap::addSpanner(Spanner* s)
155 : {
156 1513 : insert(std::pair<int, Spanner*>(s->tick().ticks(), s));
157 1513 : m_dirty = true;
158 1513 : }
159 :
160 : //---------------------------------------------------------
161 : // removeSpanner
162 : //---------------------------------------------------------
163 :
164 133 : bool SpannerMap::removeSpanner(Spanner* s)
165 : {
166 248 : for (auto i = begin(); i != end(); ++i) {
167 248 : if (i->second == s) {
168 133 : erase(i);
169 133 : m_dirty = true;
170 133 : return true;
171 : }
172 : }
173 0 : LOGD("%s (%p) not found", s->typeName(), s);
174 0 : return false;
175 : }
176 :
177 : #ifndef NDEBUG
178 : //---------------------------------------------------------
179 : // dump
180 : //---------------------------------------------------------
181 :
182 0 : void SpannerMap::dump() const
183 : {
184 0 : LOGD("SpannerMap::dump");
185 0 : for (auto i = begin(); i != end(); ++i) {
186 0 : LOGD(" %5d: %s %p", i->first, i->second->typeName(), i->second);
187 : }
188 0 : }
189 :
190 : #endif
191 : } // namespace mu::engraving
|