/* * Copyright (C) 2012 Carl Hetherington * Copyright (C) 2013-2016 Paul Davis * Copyright (C) 2014 Colin Fletcher * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef EVORAL_RANGE_HPP #define EVORAL_RANGE_HPP #include #include #include #include "evoral/visibility.h" namespace Evoral { enum /*LIBEVORAL_API*/ OverlapType { OverlapNone, // no overlap OverlapInternal, // the overlap is 100% within the object OverlapStart, // overlap covers start, but ends within OverlapEnd, // overlap begins within and covers end OverlapExternal // overlap extends to (at least) begin+end }; template /*LIBEVORAL_API*/ OverlapType coverage (T sa, T ea, T sb, T eb) { /* OverlapType returned reflects how the second (B) * range overlaps the first (A). * * The diagram shows the OverlapType of each possible relative * placement of A and B. * * Notes: * Internal: the start and end points cannot coincide * External: the start and end points can coincide * Start: end points can coincide * End: start points can coincide * * Internal disallows start and end point equality, and thus implies * that there are two disjoint portions of A which do not overlap B. * * A: |---| * B starts before A * B: |-| None * B: |--| Start * B: |----| Start * B: |------| External * B: |--------| External * B starts equal to A * B: |-| Start * B: |---| External * B: |----| External * B starts inside A * B: |-| Internal * B: |--| End * B: |---| End * B starts at end of A * B: |--| End * B starts after A * B: |-| None * A: |---| */ if (sa > ea) { // seems we are sometimes called with negative length ranges return OverlapNone; } if (sb > eb) { // seems we are sometimes called with negative length ranges return OverlapNone; } if (sb < sa) { // B starts before A if (eb < sa) { return OverlapNone; } else if (eb == sa) { return OverlapStart; } else { // eb > sa if (eb < ea) { return OverlapStart; } else if (eb == ea) { return OverlapExternal; } else { return OverlapExternal; } } } else if (sb == sa) { // B starts equal to A if (eb < ea) { return OverlapStart; } else if (eb == ea) { return OverlapExternal; } else { // eb > ea return OverlapExternal; } } else { // sb > sa if (eb < ea) { return OverlapInternal; } else if (eb == ea) { return OverlapEnd; } else { // eb > ea if (sb < ea) { // B starts inside A return OverlapEnd; } else if (sb == ea) { // B starts at end of A return OverlapEnd; } else { // sb > ea, B starts after A return OverlapNone; } } } std::cerr << "unknown overlap type!" << sa << ", " << ea << "; " << sb << ", " << eb << std::endl; assert(!"unknown overlap type!"); return OverlapNone; } /** Type to describe a time range */ template struct /*LIBEVORAL_API*/ Range { Range (T f, T t) : from (f), to (t) {} T from; ///< start of the range T to; ///< end of the range (inclusive: to lies inside the range) bool empty() const { return from == to; } T length() const { return to - from + 1; } /** for a T, return a mapping of it into the range (used for * looping). If the argument is earlier than or equal to the end of * this range, do nothing. */ T squish (T t) const { if (t > to) { t = (from + ((t - from) % length())); } return t; } }; template bool operator== (Range a, Range b) { return a.from == b.from && a.to == b.to; } template class /*LIBEVORAL_API*/ RangeList { public: RangeList () : _dirty (false) {} typedef std::list > List; List const & get () { coalesce (); return _list; } void add (Range const & range) { _dirty = true; _list.push_back (range); } bool empty () const { return _list.empty (); } void coalesce () { if (!_dirty) { return; } restart: for (typename List::iterator i = _list.begin(); i != _list.end(); ++i) { for (typename List::iterator j = _list.begin(); j != _list.end(); ++j) { if (i == j) { continue; } if (coverage (i->from, i->to, j->from, j->to) != OverlapNone) { i->from = std::min (i->from, j->from); i->to = std::max (i->to, j->to); _list.erase (j); goto restart; } } } _dirty = false; } private: List _list; bool _dirty; }; /** Type to describe the movement of a time range */ template struct /*LIBEVORAL_API*/ RangeMove { RangeMove (T f, double l, T t) : from (f), length (l), to (t) {} T from; ///< start of the range double length; ///< length of the range T to; ///< new start of the range }; /** Subtract the ranges in `sub' from that in `range', * returning the result. */ template RangeList subtract (Range range, RangeList sub) { /* Start with the input range */ RangeList result; result.add (range); if (sub.empty () || range.empty()) { return result; } typename RangeList::List s = sub.get (); /* The basic idea here is to keep a list of the result ranges, and subtract the bits of `sub' from them one by one. */ for (typename RangeList::List::const_iterator i = s.begin(); i != s.end(); ++i) { /* Here's where we'll put the new current result after subtracting *i from it */ RangeList new_result; typename RangeList::List r = result.get (); /* Work on all parts of the current result using this range *i */ for (typename RangeList::List::const_iterator j = r.begin(); j != r.end(); ++j) { switch (coverage (j->from, j->to, i->from, i->to)) { case OverlapNone: /* The thing we're subtracting (*i) does not overlap this bit of the result (*j), so pass it through. */ new_result.add (*j); break; case OverlapInternal: /* Internal overlap of the thing we're subtracting (*i) from this bit of the result, so we should end up with two bits of (*j) left over, from the start of (*j) to the start of (*i), and from the end of (*i) to the end of (*j). */ assert (j->from < i->from); assert (j->to > i->to); new_result.add (Range (j->from, i->from - 1)); new_result.add (Range (i->to + 1, j->to)); break; case OverlapStart: /* The bit we're subtracting (*i) overlaps the start of the bit of the result (*j), * so we keep only the part of of (*j) from after the end of (*i) */ assert (i->to < j->to); new_result.add (Range (i->to + 1, j->to)); break; case OverlapEnd: /* The bit we're subtracting (*i) overlaps the end of the bit of the result (*j), * so we keep only the part of of (*j) from before the start of (*i) */ assert (j->from < i->from); new_result.add (Range (j->from, i->from - 1)); break; case OverlapExternal: /* total overlap of the bit we're subtracting with the result bit, so the result bit is completely removed; do nothing */ break; } } new_result.coalesce (); result = new_result; } return result; } } #endif