forked from organicmaps/organicmaps
Replace "MergeSorted" with better one "AccumulateIntervals".
This commit is contained in:
parent
6b51c154e2
commit
06d6b1aecf
6 changed files with 280 additions and 51 deletions
|
@ -139,6 +139,7 @@ UNIT_TEST(IsIntersect_Intervals)
|
|||
TEST(!my::IsIntersect(0, 100, -50, -20), ());
|
||||
}
|
||||
|
||||
/*
|
||||
UNIT_TEST(MergeInterval_Simple)
|
||||
{
|
||||
int x0, x1;
|
||||
|
@ -206,3 +207,4 @@ UNIT_TEST(MergeSorted_NullArray)
|
|||
k = my::MergeSorted(b, 0, a, ARRAY_SIZE(a), c, ARRAY_SIZE(c));
|
||||
TEST(std::equal(c, c + k, etalon), ());
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -85,3 +85,110 @@ UNIT_TEST(STLAdd_RemoveIfKeepValid)
|
|||
TEST_EQUAL(v.size(), 4, ());
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class T, size_t N1, size_t N2, size_t N3>
|
||||
void CheckAccumulateIntervals(size_t & idTest,
|
||||
pair<T, T> (&arr1)[N1],
|
||||
pair<T, T> (&arr2)[N2],
|
||||
pair<T, T> (&arr3)[N3])
|
||||
{
|
||||
vector<pair<T, T> > res;
|
||||
AccumulateIntervals1With2(arr1, arr1 + N1, arr2, arr2 + N2, back_inserter(res));
|
||||
|
||||
++idTest;
|
||||
TEST_EQUAL(N3, res.size(), ("Test", idTest, res));
|
||||
TEST(equal(res.begin(), res.end(), arr3), ("Test", idTest, res));
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(STLAdd_AccumulateIntervals)
|
||||
{
|
||||
typedef pair<int, int> T;
|
||||
size_t idTest = 0;
|
||||
|
||||
// bound cases
|
||||
{
|
||||
vector<T> res;
|
||||
T arr[] = { T(10, 20) };
|
||||
|
||||
res.clear();
|
||||
AccumulateIntervals1With2(arr, arr + 1, arr, arr, back_inserter(res));
|
||||
TEST_EQUAL(res.size(), 1, ());
|
||||
|
||||
res.clear();
|
||||
AccumulateIntervals1With2(arr, arr, arr, arr + 1, back_inserter(res));
|
||||
TEST_EQUAL(res.size(), 0, ());
|
||||
}
|
||||
|
||||
// check splice overlapped
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40) };
|
||||
T arr2[] = { T(19, 31) };
|
||||
T res[] = { T(10, 40) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check skip not overlapped
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40) };
|
||||
T arr2[] = { T(0, 9), T(21, 29), T(41, 50) };
|
||||
T res[2] = { T(10, 20), T(30, 40) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40) };
|
||||
T arr2[] = { T(1, 2), T(3, 4), T(5, 6),
|
||||
T(21, 22), T(23, 24), T(25, 26),
|
||||
T(41, 42), T(43, 44), T(45, 46)
|
||||
};
|
||||
T res[] = { T(10, 20), T(30, 40) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check equal bounds
|
||||
{
|
||||
T arr1[] = { T(20, 30) };
|
||||
T arr2[] = { T(10, 20), T(30, 40) };
|
||||
T res[] = { T(20, 30) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40) };
|
||||
T arr2[] = { T(20, 30) };
|
||||
T res[] = { T(10, 20), T(30, 40) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check large overlap interval
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40), T(50, 60) };
|
||||
T arr2[] = { T(0, 100) };
|
||||
T res[] = { T(0, 100) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = { T(0, 100) };
|
||||
T arr2[] = { T(10, 20), T(30, 40), T(50, 60) };
|
||||
T res[] = { T(0, 100) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check splice overlapped
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40) };
|
||||
T arr2[] = { T(5, 15), T(35, 45) };
|
||||
T res[] = { T(5, 20), T(30, 45) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = { T(10, 20), T(30, 40) };
|
||||
T arr2[] = { T(1, 2), T(3, 4), T(5, 15),
|
||||
T(17, 25), T(26, 27), T(28, 32),
|
||||
T(38, 45), T(46, 50)
|
||||
};
|
||||
T res[] = { T(5, 25), T(28, 45) };
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ bool IsIntersect(T const & x0, T const & x1, T const & x2, T const & x3)
|
|||
return !((x1 < x2) || (x3 < x0));
|
||||
}
|
||||
|
||||
/*
|
||||
template <typename T>
|
||||
void Merge(T const & x0, T const & x1, T const & x2, T const & x3, T & x4, T & x5)
|
||||
{
|
||||
|
@ -179,6 +180,7 @@ size_t MergeSorted(T const * a, size_t as, T const * b, size_t bs, T * c, size_t
|
|||
|
||||
return k;
|
||||
}
|
||||
*/
|
||||
|
||||
// Computes x^n.
|
||||
template <typename T> inline T PowUint(T x, uint64_t n)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../std/iterator.hpp"
|
||||
#include "../std/map.hpp"
|
||||
|
||||
|
||||
template <class ContainerT> class BackInsertFunctor
|
||||
{
|
||||
ContainerT & m_Container;
|
||||
|
@ -168,9 +169,65 @@ template <typename IterT> IterT PrevIterInCycle(IterT it, IterT beg, IterT end)
|
|||
return --it;
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValueT, typename CompareT, typename AllocatorT>
|
||||
ValueT ValueForKey(map<KeyT, ValueT, CompareT, AllocatorT> const & m, KeyT key, ValueT defaultV)
|
||||
template <class IterT1, class IterT2, class InsertIterT>
|
||||
void AccumulateIntervals1With2(IterT1 b1, IterT1 e1, IterT2 b2, IterT2 e2, InsertIterT res)
|
||||
{
|
||||
typename map<KeyT, ValueT, CompareT, AllocatorT>::const_iterator const it = m.find(key);
|
||||
return (it == m.end() ? defaultV : it->second);
|
||||
//typedef typename iterator_traits<InsertIterT>::value_type T;
|
||||
typedef typename iterator_traits<IterT1>::value_type T;
|
||||
|
||||
T prev;
|
||||
bool validPrev = false;
|
||||
|
||||
while (b1 != e1 || b2 != e2)
|
||||
{
|
||||
// Try to continue previous range.
|
||||
if (validPrev)
|
||||
{
|
||||
// add b1 range to prev if needed
|
||||
if (b1 != e1 && b1->first < prev.second)
|
||||
{
|
||||
// correct only second if needed
|
||||
if (prev.second < b1->second)
|
||||
prev.second = b1->second;
|
||||
++b1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// add b2 range to prev if needed
|
||||
if (b2 != e2 && b2->first < prev.second)
|
||||
{
|
||||
// check that intervals are overlapped
|
||||
if (prev.first < b2->second)
|
||||
{
|
||||
// correct first and second if needed
|
||||
if (b2->first < prev.first)
|
||||
prev.first = b2->first;
|
||||
if (prev.second < b2->second)
|
||||
prev.second = b2->second;
|
||||
}
|
||||
|
||||
++b2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if nothing to add - push to results
|
||||
*res++ = prev;
|
||||
validPrev = false;
|
||||
}
|
||||
|
||||
if (b1 != e1)
|
||||
{
|
||||
// start new range
|
||||
prev = *b1++;
|
||||
validPrev = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// go to exit
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validPrev)
|
||||
*res++ = prev;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
#include "../graphics/glyph_cache.hpp"
|
||||
|
||||
#include "../base/stl_add.hpp"
|
||||
|
||||
#include "../std/iterator_facade.hpp"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct less_depth
|
||||
|
@ -108,7 +113,6 @@ namespace di
|
|||
bool hasIcon = false;
|
||||
bool hasCaptionWithoutOffset = false;
|
||||
|
||||
bool pathWasClipped = false;
|
||||
m_fontSize = 0;
|
||||
|
||||
size_t const count = keys.size();
|
||||
|
@ -149,26 +153,6 @@ namespace di
|
|||
{
|
||||
m_hasPathText = true;
|
||||
|
||||
/// calculating clip intervals only once
|
||||
|
||||
if (!pathWasClipped)
|
||||
{
|
||||
typedef gp::filter_screenpts_adapter<gp::get_path_intervals> functor_t;
|
||||
|
||||
functor_t::params p;
|
||||
|
||||
p.m_convertor = m_convertor;
|
||||
p.m_rect = m_rect;
|
||||
p.m_intervals = &m_intervals;
|
||||
|
||||
functor_t fun(p);
|
||||
|
||||
f.ForEachPointRef(fun, zoom);
|
||||
|
||||
m_pathLength = fun.m_length;
|
||||
pathWasClipped = true;
|
||||
}
|
||||
|
||||
if (!FilterTextSize(m_rules[i].m_rule))
|
||||
m_fontSize = max(m_fontSize, GetTextFontSize(m_rules[i].m_rule));
|
||||
}
|
||||
|
@ -179,9 +163,22 @@ namespace di
|
|||
hasCaptionWithoutOffset = true;
|
||||
}
|
||||
|
||||
/// placing a text on the path
|
||||
// placing a text on the path
|
||||
if (m_hasPathText && (m_fontSize != 0))
|
||||
LayoutTexts();
|
||||
{
|
||||
typedef gp::filter_screenpts_adapter<gp::get_path_intervals> functor_t;
|
||||
|
||||
functor_t::params p;
|
||||
|
||||
p.m_convertor = m_convertor;
|
||||
p.m_rect = m_rect;
|
||||
p.m_intervals = &m_intervals;
|
||||
|
||||
functor_t fun(p);
|
||||
f.ForEachPointRef(fun, zoom);
|
||||
|
||||
LayoutTexts(fun.m_length);
|
||||
}
|
||||
|
||||
if (hasIcon && hasCaptionWithoutOffset)
|
||||
// we need to delete symbol style (single one due to MakeUnique call above)
|
||||
|
@ -198,49 +195,113 @@ namespace di
|
|||
sort(m_rules.begin(), m_rules.end(), less_depth());
|
||||
}
|
||||
|
||||
void FeatureStyler::LayoutTexts()
|
||||
typedef pair<double, double> RangeT;
|
||||
template <class IterT> class RangeIterT :
|
||||
public iterator_facade<RangeIterT<IterT>, RangeT, forward_traversal_tag, RangeT>
|
||||
{
|
||||
m_textLength = m_glyphCache->getTextLength(m_fontSize, GetPathName());
|
||||
double emptySize = max(200 * m_visualScale, m_textLength);
|
||||
double minPeriodSize = emptySize + m_textLength;
|
||||
IterT m_iter;
|
||||
public:
|
||||
RangeIterT(IterT iter) : m_iter(iter) {}
|
||||
|
||||
RangeT dereference() const
|
||||
{
|
||||
IterT next = m_iter;
|
||||
++next;
|
||||
return RangeT(*m_iter, *next);
|
||||
}
|
||||
bool equal(RangeIterT const & r) const { return (m_iter == r.m_iter); }
|
||||
void increment()
|
||||
{
|
||||
++m_iter; ++m_iter;
|
||||
}
|
||||
};
|
||||
|
||||
template <class ContT> class RangeInserter
|
||||
{
|
||||
ContT & m_cont;
|
||||
public:
|
||||
RangeInserter(ContT & cont) : m_cont(cont) {}
|
||||
|
||||
RangeInserter & operator*() { return *this; }
|
||||
RangeInserter & operator++(int) { return *this; }
|
||||
RangeInserter & operator=(RangeT const & r)
|
||||
{
|
||||
m_cont.push_back(r.first);
|
||||
m_cont.push_back(r.second);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void FeatureStyler::LayoutTexts(double pathLength)
|
||||
{
|
||||
double const textLength = m_glyphCache->getTextLength(m_fontSize, GetPathName());
|
||||
/// @todo Choose best constant for minimal space.
|
||||
double const emptySize = max(200 * m_visualScale, textLength);
|
||||
// multiply on factor because tiles will be rendered in smaller scales
|
||||
double const minPeriodSize = 1.5 * (emptySize + textLength);
|
||||
|
||||
size_t textCnt = 0;
|
||||
double firstTextOffset = 0;
|
||||
|
||||
if (m_pathLength > m_textLength)
|
||||
if (pathLength > textLength)
|
||||
{
|
||||
textCnt = ceil((m_pathLength - m_textLength) / minPeriodSize);
|
||||
firstTextOffset = 0.5 * (m_pathLength - (textCnt * m_textLength + (textCnt - 1) * emptySize));
|
||||
textCnt = ceil((pathLength - textLength) / minPeriodSize);
|
||||
firstTextOffset = 0.5 * (pathLength - (textCnt * textLength + (textCnt - 1) * emptySize));
|
||||
}
|
||||
|
||||
if ((textCnt != 0) && (!m_intervals.empty()))
|
||||
if (textCnt != 0 && !m_intervals.empty())
|
||||
{
|
||||
buffer_vector<double, 16> deadZones;
|
||||
buffer_vector<RangeT, 8> deadZones;
|
||||
|
||||
for (unsigned i = 0; i < textCnt; ++i)
|
||||
for (size_t i = 0; i < textCnt; ++i)
|
||||
{
|
||||
double deadZoneStart = firstTextOffset + (m_textLength + minPeriodSize) * i;
|
||||
double deadZoneEnd = deadZoneStart + m_textLength;
|
||||
double const deadZoneStart = firstTextOffset + minPeriodSize * i;
|
||||
double const deadZoneEnd = deadZoneStart + textLength;
|
||||
|
||||
if (deadZoneStart > m_intervals.back())
|
||||
break;
|
||||
|
||||
deadZones.push_back(deadZoneStart);
|
||||
deadZones.push_back(deadZoneEnd);
|
||||
m_offsets.push_back(deadZoneStart);
|
||||
deadZones.push_back(make_pair(deadZoneStart, deadZoneEnd));
|
||||
}
|
||||
|
||||
if (!deadZones.empty())
|
||||
{
|
||||
buffer_vector<double, 16> res(deadZones.size() + m_intervals.size());
|
||||
buffer_vector<double, 16> res;
|
||||
|
||||
int k = my::MergeSorted(&m_intervals[0], m_intervals.size(),
|
||||
&deadZones[0], deadZones.size(),
|
||||
&res[0], res.size());
|
||||
|
||||
res.resize(k);
|
||||
// accumulate text layout intervals with cliping intervals
|
||||
typedef RangeIterT<ClipIntervalsT::iterator> IterT;
|
||||
AccumulateIntervals1With2(IterT(m_intervals.begin()), IterT(m_intervals.end()),
|
||||
deadZones.begin(), deadZones.end(),
|
||||
RangeInserter<ClipIntervalsT>(res));
|
||||
|
||||
m_intervals = res;
|
||||
ASSERT_EQUAL(m_intervals.size() % 2, 0, ());
|
||||
|
||||
// get final text offsets (belongs to final clipping intervals)
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
while (i != deadZones.size() && j != m_intervals.size())
|
||||
{
|
||||
ASSERT_LESS(deadZones[i].first, deadZones[i].second, ());
|
||||
ASSERT_LESS(m_intervals[j], m_intervals[j+1], ());
|
||||
|
||||
if (deadZones[i].first < m_intervals[j])
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (m_intervals[j+1] <= deadZones[i].first)
|
||||
{
|
||||
j += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
ASSERT_LESS_OR_EQUAL(m_intervals[j], deadZones[i].first, ());
|
||||
ASSERT_LESS_OR_EQUAL(deadZones[i].second, m_intervals[j+1], ());
|
||||
|
||||
m_offsets.push_back(deadZones[i].first);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,6 @@ namespace di
|
|||
typedef buffer_vector<double, 8> TextOffsetsT;
|
||||
TextOffsetsT m_offsets;
|
||||
|
||||
double m_pathLength;
|
||||
double m_textLength;
|
||||
uint8_t m_fontSize;
|
||||
|
||||
graphics::GlyphCache * m_glyphCache;
|
||||
|
@ -80,7 +78,9 @@ namespace di
|
|||
string const GetPathName() const;
|
||||
|
||||
bool FilterTextSize(drule::BaseRule const * pRule) const;
|
||||
|
||||
private:
|
||||
uint8_t GetTextFontSize(drule::BaseRule const * pRule) const;
|
||||
void LayoutTexts();
|
||||
void LayoutTexts(double pathLength);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue