Implement EncodePolyline() and DecodePolyline() + unit tests.

This commit is contained in:
Yury Melnichek 2011-01-29 22:32:11 +01:00 committed by Alex Zolotarev
parent 490f5c3dff
commit f3da0c3ca1
4 changed files with 402 additions and 8 deletions

View file

@ -1,2 +1,233 @@
#include "geometry_coding.hpp"
#include "../coding/byte_stream.hpp"
#include "../coding/varint.hpp"
#include "../base/assert.hpp"
#include "../base/bits.hpp"
#include "../base/stl_add.hpp"
#include "../std/complex.hpp"
#include "../std/vector.hpp"
namespace
{
inline uint64_t EncodeDelta(m2::PointU const & actual, m2::PointU const & prediction)
{
return bits::BitwiseMerge(
bits::ZigZagEncode(static_cast<int32_t>(actual.x) - static_cast<int32_t>(prediction.x)),
bits::ZigZagEncode(static_cast<int32_t>(actual.y) - static_cast<int32_t>(prediction.y)));
}
inline m2::PointU DecodeDelta(uint64_t delta, m2::PointU const & prediction)
{
uint32_t x, y;
bits::BitwiseSplit(delta, x, y);
return m2::PointU(prediction.x + bits::ZigZagDecode(x), prediction.y + bits::ZigZagDecode(y));
}
inline void EncodeVarUints(vector<uint64_t> const & varints, vector<char> & serialOutput)
{
PushBackByteSink<vector<char> > sink(serialOutput);
for (vector<uint64_t>::const_iterator it = varints.begin(); it != varints.end(); ++it)
WriteVarUint(sink, *it);
}
template <typename T>
inline m2::PointU ClampPoint(m2::PointU const & maxPoint, m2::Point<T> point)
{
return m2::PointU(
point.x < 0 ? 0 : (point.x < maxPoint.x ? static_cast<uint32_t>(point.x) : maxPoint.x),
point.y < 0 ? 0 : (point.y < maxPoint.y ? static_cast<uint32_t>(point.y) : maxPoint.y));
}
bool TestDecoding(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput,
void (* fnDecode)(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points))
{
vector<m2::PointU> decoded;
decoded.reserve(points.size());
fnDecode(&serialOutput[0], &serialOutput[0] + serialOutput.size(), basePoint, maxPoint, decoded);
ASSERT_EQUAL(points, decoded, (basePoint, maxPoint));
return true;
}
}
m2::PointU PredictPointInPolyline(m2::PointU const & maxPoint,
m2::PointU const & p1,
m2::PointU const & p2)
{
return ClampPoint(maxPoint, m2::PointI64(p1) + m2::PointI64(p1) - m2::PointI64(p2));
}
m2::PointU PredictPointInPolyline(m2::PointU const & maxPoint,
m2::PointU const & p1,
m2::PointU const & p2,
m2::PointU const & p3)
{
CHECK_NOT_EQUAL(p2, p3, ());
// In complex numbers:
// Ci = Ci-1 + (Ci-1 - Ci-2) * (Ci-1 - Ci-2) / (Ci-2 - Ci-3)
complex<double> const c1(p1.x, p1.y);
complex<double> const c2(p2.x, p2.y);
complex<double> const c3(p3.x, p3.y);
complex<double> const c0 = c1 + (c1 - c2) * (c1 - c2) / (c2 - c3);
return ClampPoint(maxPoint, m2::PointD(c0.real(), c0.imag()));
}
void EncodePolylinePrev1(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & /*maxPoint*/,
vector<char> & serialOutput)
{
vector<uint64_t> deltas;
deltas.reserve(points.size());
if (points.size() > 0)
{
deltas.push_back(EncodeDelta(points[0], basePoint));
for (size_t i = 1; i < points.size(); ++i)
deltas.push_back(EncodeDelta(points[i], points[i-1]));
}
EncodeVarUints(deltas, serialOutput);
ASSERT(TestDecoding(points, basePoint, m2::PointU(), serialOutput, &DecodePolylinePrev1), ());
}
void DecodePolylinePrev1(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & /*maxPoint*/,
vector<m2::PointU> & points)
{
vector<uint64_t> deltas;
ReadVarUint64Array(pBeg, pEnd, MakeBackInsertFunctor(deltas));
points.reserve(points.size() + deltas.size());
if (deltas.size() > 0)
{
points.push_back(DecodeDelta(deltas[0], basePoint));
for (size_t i = 1; i < deltas.size(); ++i)
points.push_back(DecodeDelta(deltas[i], points.back()));
}
}
void EncodePolylinePrev2(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput)
{
vector<uint64_t> deltas;
deltas.reserve(points.size());
if (points.size() > 0)
{
deltas.push_back(EncodeDelta(points[0], basePoint));
if (points.size() > 1)
{
deltas.push_back(EncodeDelta(points[1], points[0]));
for (size_t i = 2; i < points.size(); ++i)
deltas.push_back(EncodeDelta(points[i],
PredictPointInPolyline(maxPoint, points[i-1], points[i-2])));
}
}
EncodeVarUints(deltas, serialOutput);
ASSERT(TestDecoding(points, basePoint, maxPoint, serialOutput, &DecodePolylinePrev2), ());
}
void DecodePolylinePrev2(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points)
{
vector<uint64_t> deltas;
ReadVarUint64Array(pBeg, pEnd, MakeBackInsertFunctor(deltas));
points.reserve(points.size() + deltas.size());
if (deltas.size() > 0)
{
points.push_back(DecodeDelta(deltas[0], basePoint));
if (deltas.size() > 1)
{
points.push_back(DecodeDelta(deltas[1], points.back()));
for (size_t i = 2; i < deltas.size(); ++i)
{
size_t const n = points.size();
points.push_back(DecodeDelta(deltas[i],
PredictPointInPolyline(maxPoint, points[n-1], points[n-2])));
}
}
}
}
void EncodePolylinePrev3(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput)
{
ASSERT_LESS_OR_EQUAL(basePoint.x, maxPoint.x, (basePoint, maxPoint));
ASSERT_LESS_OR_EQUAL(basePoint.y, maxPoint.y, (basePoint, maxPoint));
vector<uint64_t> deltas;
deltas.reserve(points.size());
if (points.size() > 0)
{
deltas.push_back(EncodeDelta(points[0], basePoint));
if (points.size() > 1)
{
deltas.push_back(EncodeDelta(points[1], points[0]));
if (points.size() > 2)
{
m2::PointU const prediction = PredictPointInPolyline(maxPoint, points[1], points[0]);
deltas.push_back(EncodeDelta(points[2], prediction));
for (size_t i = 3; i < points.size(); ++i)
{
m2::PointU const prediction =
PredictPointInPolyline(maxPoint, points[i-1], points[i-2], points[i-3]);
deltas.push_back(EncodeDelta(points[i], prediction));
}
}
}
}
EncodeVarUints(deltas, serialOutput);
ASSERT(TestDecoding(points, basePoint, maxPoint, serialOutput, &DecodePolylinePrev3), ());
}
void DecodePolylinePrev3(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points)
{
ASSERT_LESS_OR_EQUAL(basePoint.x, maxPoint.x, (basePoint, maxPoint));
ASSERT_LESS_OR_EQUAL(basePoint.y, maxPoint.y, (basePoint, maxPoint));
vector<uint64_t> deltas;
ReadVarUint64Array(pBeg, pEnd, MakeBackInsertFunctor(deltas));
points.reserve(points.size() + deltas.size());
if (deltas.size() > 0)
{
points.push_back(DecodeDelta(deltas[0], basePoint));
if (deltas.size() > 1)
{
m2::PointU const pt0 = points.back();
points.push_back(DecodeDelta(deltas[1], pt0));
if (deltas.size() > 2)
{
points.push_back(DecodeDelta(deltas[2],
PredictPointInPolyline(maxPoint, points.back(), pt0)));
for (size_t i = 3; i < deltas.size(); ++i)
{
size_t const n = points.size();
m2::PointU const prediction =
PredictPointInPolyline(maxPoint, points[n-1], points[n-2], points[n-3]);
points.push_back(DecodeDelta(deltas[i], prediction));
}
}
}
}
}

View file

@ -5,15 +5,63 @@
#include "../std/vector.hpp"
#include "../std/tuple.hpp"
void EncodePolyline(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput);
// Predict point p0 given previous (p1, p2).
m2::PointU PredictPointInPolyline(m2::PointU const & maxPoint,
m2::PointU const & p1,
m2::PointU const & p2);
void DecodePolyline(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points);
// Predict point p0 given previous (p1, p2, p3).
m2::PointU PredictPointInPolyline(m2::PointU const & maxPoint,
m2::PointU const & p1,
m2::PointU const & p2,
m2::PointU const & p3);
void EncodePolylinePrev1(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput);
void DecodePolylinePrev1(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points);
void EncodePolylinePrev2(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput);
void DecodePolylinePrev2(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points);
void EncodePolylinePrev3(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput);
void DecodePolylinePrev3(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points);
inline void EncodePolyline(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput)
{
EncodePolylinePrev3(points, basePoint, maxPoint, serialOutput);
}
inline void DecodePolyline(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points)
{
DecodePolylinePrev3(pBeg, pEnd, basePoint, maxPoint, points);
}
void EncodeTriangles(vector<m2::PointU> const & points,
vector<tuple<uint32_t, uint32_t, uint32_t> > const & triangles,

View file

@ -0,0 +1,114 @@
#include "../geometry_coding.hpp"
#include "../../testing/testing.hpp"
#include "../../geometry/geometry_tests/large_polygon.hpp"
#include "../../geometry/distance.hpp"
#include "../../geometry/simplification.hpp"
#include "../../base/logging.hpp"
typedef m2::PointU PU;
UNIT_TEST(PredictPointsInPolyline2)
{
TEST_EQUAL(PU(7, 6), PredictPointInPolyline(PU(8, 7), PU(4, 4), PU(1, 2)), ());
}
UNIT_TEST(PredictPointsInPolyline2_ClampMax)
{
TEST_EQUAL(PU(6, 6), PredictPointInPolyline(PU(6, 7), PU(4, 4), PU(1, 2)), ());
TEST_EQUAL(PU(7, 6), PredictPointInPolyline(PU(8, 7), PU(4, 4), PU(1, 2)), ());
TEST_EQUAL(PU(5, 5), PredictPointInPolyline(PU(5, 5), PU(4, 4), PU(1, 2)), ());
}
UNIT_TEST(PredictPointsInPolyline2_Clamp0)
{
TEST_EQUAL(PU(4, 0), PredictPointInPolyline(PU(5, 5), PU(4, 1), PU(4, 4)), ());
}
UNIT_TEST(PredictPointsInPolyline3_Square)
{
TEST_EQUAL(PU(5, 1), PredictPointInPolyline(PU(6, 6), PU(5, 4), PU(2, 4), PU(2, 1)), ());
TEST_EQUAL(PU(5, 3), PredictPointInPolyline(PU(6, 6), PU(4, 1), PU(2, 2), PU(3, 4)), ());
}
UNIT_TEST(PredictPointsInPolyline3_SquareClamp0)
{
TEST_EQUAL(PU(5, 1), PredictPointInPolyline(PU(6, 6), PU(5, 4), PU(2, 4), PU(2, 1)), ());
TEST_EQUAL(PU(4, 0), PredictPointInPolyline(PU(6, 6), PU(2, 0), PU(3, 2), PU(5, 1)), ());
}
UNIT_TEST(PredictPointsInPolyline3_90deg)
{
TEST_EQUAL(PU(3, 2), PredictPointInPolyline(PU(8, 8), PU(3, 6), PU(1, 6), PU(1, 5)), ());
}
namespace
{
void TestPolylineEncode(char const * testName,
vector<m2::PointU> const & points,
m2::PointU const & maxPoint,
void (* fnEncode)(vector<m2::PointU> const & points,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<char> & serialOutput),
void (* fnDecode)(char const * pBeg, char const * pEnd,
m2::PointU const & basePoint,
m2::PointU const & maxPoint,
vector<m2::PointU> & points))
{
m2::PointU const basePoint = (points.empty() ? m2::PointU(0, 0) : points[points.size() / 2]);
vector<char> data;
fnEncode(points, basePoint, maxPoint, data);
vector<m2::PointU> decodedPoints;
// TODO: push_back
fnDecode(&data[0], &data[0] + data.size(), basePoint, maxPoint, decodedPoints);
TEST_EQUAL(points, decodedPoints, ());
if (points.size() > 10)
LOG(LINFO, (testName, points.size(), data.size()));
}
vector<m2::PointU> SimplifyPoints(vector<m2::PointU> const & points, double eps)
{
vector<m2::PointU> simpPoints;
typedef mn::DistanceToLineSquare<m2::PointD> DistanceF;
SimplifyNearOptimal<DistanceF>(20, points.begin(), points.end(), eps,
AccumulateSkipSmallTrg<DistanceF, m2::PointU>(simpPoints, eps));
return simpPoints;
}
void TestEncodePolyline(char const * name, m2::PointU maxPoint, vector<m2::PointU> const & points)
{
TestPolylineEncode(name, points, maxPoint, &EncodePolylinePrev1, &DecodePolylinePrev1);
TestPolylineEncode(name, points, maxPoint, &EncodePolylinePrev2, &DecodePolylinePrev2);
TestPolylineEncode(name, points, maxPoint, &EncodePolylinePrev3, &DecodePolylinePrev3);
}
}
UNIT_TEST(EncodePolyline)
{
size_t const kSizes [] = { 0, 1, 2, 3, 4, ARRAY_SIZE(kLargePolygon) };
m2::PointU const maxPoint(1000000000, 1000000000);
for (size_t iSize = 0; iSize < ARRAY_SIZE(kSizes); ++iSize)
{
size_t const polygonSize = kSizes[iSize];
vector<m2::PointU> points;
points.reserve(polygonSize);
for (size_t i = 0; i < polygonSize; ++i)
points.push_back(m2::PointU(static_cast<uint32_t>(kLargePolygon[i].x * 1000),
static_cast<uint32_t>((kLargePolygon[i].y + 200) * 1000)));
TestEncodePolyline("Unsimp", maxPoint, points);
TestEncodePolyline("1simp", maxPoint, SimplifyPoints(points, 1));
TestEncodePolyline("2simp", maxPoint, SimplifyPoints(points, 2));
TestEncodePolyline("4simp", maxPoint, SimplifyPoints(points, 4));
TestEncodePolyline("10simp", maxPoint, SimplifyPoints(points, 10));
TestEncodePolyline("100simp", maxPoint, SimplifyPoints(points, 100));
TestEncodePolyline("500simp", maxPoint, SimplifyPoints(points, 500));
TestEncodePolyline("1000simp", maxPoint, SimplifyPoints(points, 1000));
TestEncodePolyline("2000simp", maxPoint, SimplifyPoints(points, 2000));
TestEncodePolyline("4000simp", maxPoint, SimplifyPoints(points, 4000));
}
}

View file

@ -37,4 +37,5 @@ SOURCES += \
data_header_test.cpp \
feature_bucketer_test.cpp \
feature_routine.cpp \
geometry_coding_test.cpp