/****************************************************************************** * File: RangeMap.impl.h * Date: 17.5.2018 *****************************************************************************/ #include "Grinder.h" #include "RangeMap.h" #include "util/StringConv.h" template<typename ValType> void RangeMap<ValType>::add(value_type at, const range_type& range) { _rangeMap[at] += range; } template<typename ValType> void RangeMap<ValType>::add(value_type at, const range_vector_type& ranges) { _rangeMap[at] += ranges; } template<typename ValType> void RangeMap<ValType>::add(const RangeMap<value_type>& other) { for (const auto& rangeMapEntry : other._rangeMap) add(rangeMapEntry.first, rangeMapEntry.second); } template<typename ValType> void RangeMap<ValType>::remove(value_type at, const range_type& range) { if (_rangeMap.find(at) != _rangeMap.end()) { _rangeMap[at] -= range; if (_rangeMap[at].empty()) _rangeMap.erase(at); } } template<typename ValType> void RangeMap<ValType>::remove(value_type at, const range_vector_type& ranges) { _rangeMap[at] -= ranges; } template<typename ValType> void RangeMap<ValType>::remove(const RangeMap<value_type>& other) { for (const auto& rangeMapEntry : other._rangeMap) remove(rangeMapEntry.first, rangeMapEntry.second); } template<typename ValType> void RangeMap<ValType>::moveTo(value_type x, value_type y) { auto mapBounds = bounds(); auto xOffset = x - mapBounds.first.first; auto yOffset = y - mapBounds.second.first; if (xOffset != 0) { // Offset every range in the map for (auto& rangeRow : _rangeMap) rangeRow.second.offsetRanges(xOffset); } if (yOffset != 0) { // Build a new map that is offset by the given amount range_map_type newMap; for (auto it = _rangeMap.begin(); it != _rangeMap.end(); ++it) newMap[it->first + yOffset] = std::move(_rangeMap[it->first]); _rangeMap = std::move(newMap); } } template<typename ValType> bool RangeMap<ValType>::contains(value_type at) const { return find(at) != _rangeMap.cend(); } template<typename ValType> bool RangeMap<ValType>::contains(value_type at, value_type value) const { if (_rangeMap.find(at) != _rangeMap.cend()) return _rangeMap.at(at).contains(value); else return false; } template<typename ValType> bool RangeMap<ValType>::adjoins(value_type at, const range_type& range) const { // Ranges can overlap or touch, so look at the row above and below the given row as well for (int offset = -1; offset <= 1; ++offset) { auto it = _rangeMap.find(static_cast<value_type>(at + offset)); if (it != _rangeMap.cend()) { if (it->second.adjoins(range)) return true; } } return false; } template<typename ValType> auto RangeMap<ValType>::bounds() const { std::pair<value_type, value_type> boundsX{std::numeric_limits<value_type>::max(), std::numeric_limits<value_type>::min()}; std::pair<value_type, value_type> boundsY{std::numeric_limits<value_type>::max(), std::numeric_limits<value_type>::min()}; for (const auto& rangeMapEntry : _rangeMap) { // Get bounds in x direction auto rangeBounds = rangeMapEntry.second.bounds(); if (rangeBounds.first < boundsX.first) boundsX.first = rangeBounds.first; if (rangeBounds.second > boundsX.second) boundsX.second = rangeBounds.second; // Get bounds in y direction if (rangeMapEntry.first < boundsY.first) boundsY.first = rangeMapEntry.first; if (rangeMapEntry.first > boundsY.second) boundsY.second = rangeMapEntry.first; } return std::make_pair(boundsX, boundsY); } template<typename ValType> auto RangeMap<ValType>::regions() const { using RegionsType = std::list<RangeMap<value_type>>; RegionsType regions; // Iterate over all ranges in each row for (auto rangeRow : _rangeMap) { for (const auto& range : rangeRow.second) { std::list<typename RegionsType::iterator> adjoiningRegions; // Find all adjoining regions of the current range for (auto it = regions.begin(); it != regions.end(); ++it) { if (it->adjoins(rangeRow.first, range)) adjoiningRegions.push_back(it); } if (adjoiningRegions.size() > 1) // Merge regions { RangeMap<value_type> mergedRegion; // Merge the adjoining regions and remove each one, as they will be replaced by the new region for (auto it : adjoiningRegions) { mergedRegion.add(*it); regions.erase(it); } mergedRegion.add(rangeRow.first, range); regions.push_back(std::move(mergedRegion)); } else if (adjoiningRegions.size() == 1) // Extend region { adjoiningRegions.front()->add(rangeRow.first, range); } else // Create a new region { RangeMap<value_type> newRegion; newRegion.add(rangeRow.first, range); regions.push_back(std::move(newRegion)); } } } return regions; } template<typename ValType> QString RangeMap<ValType>::toString() const { QStringList rangeMap; for (const auto& rangeMapEntry : _rangeMap) rangeMap << QString{"%1:%2"}.arg(StringConv::convertValue(rangeMapEntry.first)).arg(rangeMapEntry.second.toString()); return rangeMap.join("|"); } template<typename ValType> void RangeMap<ValType>::fromString(const QString& str) { _rangeMap.clear(); for (auto rangeVector : str.split("|", Qt::SkipEmptyParts)) { QStringList tokens = rangeVector.split(":"); if (tokens.size() == 2) _rangeMap[StringConv::convertString<value_type>(tokens[0])] = tokens[1]; } }