Skip to content
Snippets Groups Projects
RangeMap.impl.h 5.29 KiB
Newer Older
/******************************************************************************
 * File: RangeMap.impl.h
 * Date: 17.5.2018
 *****************************************************************************/

#include "Grinder.h"
#include "RangeMap.h"
#include "util/StringConv.h"

template<typename ValType>
Daniel Müller's avatar
Daniel Müller committed
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)
{
Daniel Müller's avatar
Daniel Müller committed
	_rangeMap[at] += ranges;
}

template<typename ValType>
void RangeMap<ValType>::add(const RangeMap<value_type>& other)
{
Daniel Müller's avatar
Daniel Müller committed
	for (const auto& rangeMapEntry : other._rangeMap)
		add(rangeMapEntry.first, rangeMapEntry.second);
template<typename ValType>
Daniel Müller's avatar
Daniel Müller committed
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)
{
Daniel Müller's avatar
Daniel Müller committed
	_rangeMap[at] -= ranges;
}

template<typename ValType>
void RangeMap<ValType>::remove(const RangeMap<value_type>& other)
{
Daniel Müller's avatar
Daniel Müller committed
	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()};

Daniel Müller's avatar
Daniel Müller committed
	for (const auto& rangeMapEntry : _rangeMap)
	{
		// Get bounds in x direction
Daniel Müller's avatar
Daniel Müller committed
		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
Daniel Müller's avatar
Daniel Müller committed
		if (rangeMapEntry.first < boundsY.first)
			boundsY.first = rangeMapEntry.first;
Daniel Müller's avatar
Daniel Müller committed
		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));
template<typename ValType>
QString RangeMap<ValType>::toString() const
{
	QStringList rangeMap;

Daniel Müller's avatar
Daniel Müller committed
	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];
	}
}