1 /* This file is part of Evoral.
2 * Copyright (C) 2008 David Robillard <http://drobilla.net>
3 * Copyright (C) 2000-2008 Paul Davis
5 * Evoral is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option) any later
10 * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #ifndef EVORAL_RANGE_HPP
20 #define EVORAL_RANGE_HPP
25 #include "evoral/visibility.h"
31 enum /*LIBEVORAL_API*/ OverlapType {
32 OverlapNone, // no overlap
33 OverlapInternal, // the overlap is 100% within the object
34 OverlapStart, // overlap covers start, but ends within
35 OverlapEnd, // overlap begins within and covers end
36 OverlapExternal // overlap extends to (at least) begin+end
40 /*LIBEVORAL_API*/ OverlapType coverage (T sa, T ea, T sb, T eb) {
41 /* OverlapType returned reflects how the second (B)
42 * range overlaps the first (A).
44 * The diagram shows the OverlapType of each possible relative
45 * placement of A and B.
48 * Internal: the start and end points cannot coincide
49 * External: the start and end points can coincide
50 * Start: end points can coincide
51 * End: start points can coincide
53 * Internal disallows start and end point equality, and thus implies
54 * that there are two disjoint portions of A which do not overlap B.
61 * B: |------| External
62 * B: |--------| External
71 * B starts at end of A
80 // seems we are sometimes called with negative length ranges
85 // seems we are sometimes called with negative length ranges
89 if (sb < sa) { // B starts before A
92 } else if (eb == sa) {
97 } else if (eb == ea) {
98 return OverlapExternal;
100 return OverlapExternal;
103 } else if (sb == sa) { // B starts equal to A
106 } else if (eb == ea) {
107 return OverlapExternal;
109 return OverlapExternal;
113 return OverlapInternal;
114 } else if (eb == ea) {
117 if (sb < ea) { // B starts inside A
119 } else if (sb == ea) { // B starts at end of A
121 } else { // sb > ea, B starts after A
127 std::cerr << "unknown overlap type!" << sa << ", " << ea << "; " << sb << ", " << eb << std::endl;
128 assert(!"unknown overlap type!");
132 /** Type to describe a time range */
134 struct /*LIBEVORAL_API*/ Range {
135 Range (T f, T t) : from (f), to (t) {}
136 T from; ///< start of the range
137 T to; ///< end of the range (inclusive: to lies inside the range)
138 bool empty() const { return from == to; }
139 T length() const { return to - from + 1; }
141 /** for a T, return a mapping of it into the range (used for
142 * looping). If the argument is earlier than or equal to the end of
143 * this range, do nothing.
145 T squish (T t) const {
147 t = (from + ((t - from) % length()));
154 bool operator== (Range<T> a, Range<T> b) {
155 return a.from == b.from && a.to == b.to;
159 class /*LIBEVORAL_API*/ RangeList {
161 RangeList () : _dirty (false) {}
163 typedef std::list<Range<T> > List;
165 List const & get () {
170 void add (Range<T> const & range) {
172 _list.push_back (range);
175 bool empty () const {
176 return _list.empty ();
185 for (typename List::iterator i = _list.begin(); i != _list.end(); ++i) {
186 for (typename List::iterator j = _list.begin(); j != _list.end(); ++j) {
192 if (coverage (i->from, i->to, j->from, j->to) != OverlapNone) {
193 i->from = std::min (i->from, j->from);
194 i->to = std::max (i->to, j->to);
210 /** Type to describe the movement of a time range */
212 struct /*LIBEVORAL_API*/ RangeMove {
213 RangeMove (T f, double l, T t) : from (f), length (l), to (t) {}
214 T from; ///< start of the range
215 double length; ///< length of the range
216 T to; ///< new start of the range
219 /** Subtract the ranges in `sub' from that in `range',
220 * returning the result.
223 RangeList<T> subtract (Range<T> range, RangeList<T> sub)
225 /* Start with the input range */
229 if (sub.empty () || range.empty()) {
233 typename RangeList<T>::List s = sub.get ();
235 /* The basic idea here is to keep a list of the result ranges, and subtract
236 the bits of `sub' from them one by one.
239 for (typename RangeList<T>::List::const_iterator i = s.begin(); i != s.end(); ++i) {
241 /* Here's where we'll put the new current result after subtracting *i from it */
242 RangeList<T> new_result;
244 typename RangeList<T>::List r = result.get ();
246 /* Work on all parts of the current result using this range *i */
247 for (typename RangeList<T>::List::const_iterator j = r.begin(); j != r.end(); ++j) {
249 switch (coverage (j->from, j->to, i->from, i->to)) {
251 /* The thing we're subtracting (*i) does not overlap this bit of the result (*j),
256 case OverlapInternal:
257 /* Internal overlap of the thing we're subtracting (*i) from this bit of the result,
258 so we should end up with two bits of (*j) left over, from the start of (*j) to
259 the start of (*i), and from the end of (*i) to the end of (*j).
261 assert (j->from < i->from);
262 assert (j->to > i->to);
263 new_result.add (Range<T> (j->from, i->from - 1));
264 new_result.add (Range<T> (i->to + 1, j->to));
267 /* The bit we're subtracting (*i) overlaps the start of the bit of the result (*j),
268 * so we keep only the part of of (*j) from after the end of (*i)
270 assert (i->to < j->to);
271 new_result.add (Range<T> (i->to + 1, j->to));
274 /* The bit we're subtracting (*i) overlaps the end of the bit of the result (*j),
275 * so we keep only the part of of (*j) from before the start of (*i)
277 assert (j->from < i->from);
278 new_result.add (Range<T> (j->from, i->from - 1));
280 case OverlapExternal:
281 /* total overlap of the bit we're subtracting with the result bit, so the
282 result bit is completely removed; do nothing */
287 new_result.coalesce ();