diff --git a/geometry/geometry.pro b/geometry/geometry.pro index 13606ae28e..769b4df34d 100644 --- a/geometry/geometry.pro +++ b/geometry/geometry.pro @@ -12,7 +12,6 @@ include($$ROOT_DIR/common.pri) SOURCES += \ screenbase.cpp \ packer.cpp \ - tesselator.cpp \ HEADERS += \ rect2d.hpp \ @@ -30,5 +29,4 @@ HEADERS += \ transformations.hpp \ tree4d.hpp \ polygon.hpp \ - region2d.hpp \ - tesselator.hpp \ + region2d.hpp \ diff --git a/geometry/tesselator.cpp b/geometry/tesselator.cpp deleted file mode 100644 index 67819579a8..0000000000 --- a/geometry/tesselator.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "tesselator.hpp" - -#include "../base/assert.hpp" - -#include "../../3party/sgitess/interface.h" - -namespace tesselator -{ - struct AddTessPointF - { - tess::Tesselator & m_tess; - AddTessPointF(tess::Tesselator & tess) : m_tess(tess) - {} - void operator()(m2::PointD const & p) - { - m_tess.add(tess::Vertex(p.x, p.y)); - } - }; - - void TesselateInterior(points_container const & bound, holes_container const & holes, - points_container & triangles) - { - tess::VectorDispatcher disp; - tess::Tesselator tess; - tess.setDispatcher(&disp); - tess.setWindingRule(tess::WindingOdd); - - tess.beginPolygon(); - - tess.beginContour(); - for_each(bound.begin(), bound.end(), AddTessPointF(tess)); - tess.endContour(); - - for (holes_container::const_iterator it = holes.begin(); it != holes.end(); ++it) - { - tess.beginContour(); - for_each(it->begin(), it->end(), AddTessPointF(tess)); - tess.endContour(); - } - - tess.endPolygon(); - - for (size_t i = 0; i < disp.indices().size(); ++i) - { - switch (disp.indices()[i].first) - { - case tess::TrianglesFan: - case tess::TrianglesStrip: - case tess::LineLoop: - ASSERT(0, ("We've got invalid type during teselation:", disp.indices()[i].first)); - case tess::TrianglesList: break; - } - - for (size_t j = 0; j < disp.indices()[i].second.size(); ++j) - { - int const idx = disp.indices()[i].second[j]; - tess::Vertex const & v = disp.vertices()[idx]; - triangles.push_back(m2::PointD(v.x, v.y)); - } - - ASSERT_EQUAL(triangles.size() % 3, 0, ()); - } - } -} diff --git a/geometry/tesselator.hpp b/geometry/tesselator.hpp deleted file mode 100644 index acb547f997..0000000000 --- a/geometry/tesselator.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "point2d.hpp" - -#include "../std/list.hpp" - -namespace tesselator -{ - typedef vector points_container; - typedef list holes_container; - void TesselateInterior(points_container const & bound, holes_container const & holes, - points_container & triangles); -} diff --git a/indexer/feature.cpp b/indexer/feature.cpp index 511a621bda..4d40732657 100644 --- a/indexer/feature.cpp +++ b/indexer/feature.cpp @@ -868,7 +868,7 @@ uint32_t FeatureType::ParseTriangles(int scale) const ReaderSource src( m_cont->GetReader(feature::GetTagForIndex(TRIANGLE_FILE_TAG, ind))); src.Skip(m_trgOffsets[ind]); - feature::LoadTriangles(m_Triangles, m_base, src); + serial::LoadOuterTriangles(src, m_base, m_Triangles); sz = static_cast(src.Pos() - m_trgOffsets[ind]); } diff --git a/indexer/geometry_serialization.cpp b/indexer/geometry_serialization.cpp index 6738417d76..3eadcfcf6b 100644 --- a/indexer/geometry_serialization.cpp +++ b/indexer/geometry_serialization.cpp @@ -7,6 +7,9 @@ #include "../std/algorithm.hpp" #include "../std/iterator.hpp" +#include "../std/stack.hpp" + +#include "../base/start_mem_debug.hpp" namespace serial @@ -35,9 +38,9 @@ namespace serial } } - void Encode(EncodeFunT fn, vector const & points, int64_t base, vector & deltas) + void Encode(EncodeFunT fn, vector const & points, int64_t base, DeltasT & deltas) { - vector upoints; + PointsT upoints; upoints.reserve(points.size()); transform(points.begin(), points.end(), back_inserter(upoints), &pts::D2U); @@ -45,20 +48,20 @@ namespace serial (*fn)(upoints, pts::GetBasePoint(base), pts::GetMaxPoint(), deltas); } - void Decode(DecodeFunT fn, vector const & deltas, int64_t base, OutPointsT & points) + void Decode(DecodeFunT fn, DeltasT const & deltas, int64_t base, OutPointsT & points) { - vector upoints; + PointsT upoints; upoints.reserve(deltas.size()); (*fn)(deltas, pts::GetBasePoint(base), pts::GetMaxPoint(), upoints); - points.reserve(upoints.size()); + // Don't make reserve for points. It may be not empty. transform(upoints.begin(), upoints.end(), back_inserter(points), &pts::U2D); } void const * LoadInner(DecodeFunT fn, void const * pBeg, size_t count, int64_t base, OutPointsT & points) { - vector deltas; + DeltasT deltas; deltas.reserve(count); void const * ret = ReadVarUint64Array(static_cast(pBeg), count, MakeBackInsertFunctor(deltas)); @@ -66,4 +69,162 @@ namespace serial Decode(fn, deltas, base, points); return ret; } + + + TrianglesChainSaver::TrianglesChainSaver(int64_t base) + { + m_base = pts::GetBasePoint(base); + m_max = pts::GetMaxPoint(); + } + + namespace + { + struct edge_less_p0 + { + typedef tesselator::Edge edge_t; + + bool operator() (edge_t const & e1, edge_t const & e2) const + { + return (e1.m_p[0] == e2.m_p[0]) ? (e1.m_side < e2.m_side) : (e1.m_p[0] < e2.m_p[0]); + } + bool operator() (edge_t const & e1, int e2) const + { + return e1.m_p[0] < e2; + } + bool operator() (int e1, edge_t const & e2) const + { + return e1 < e2.m_p[0]; + } + }; + } + + void TrianglesChainSaver::operator() (PointT arr[3], vector edges) + { + m_buffers.push_back(BufferT()); + MemWriter writer(m_buffers.back()); + + WriteVarUint(writer, EncodeDelta(arr[0], m_base)); + WriteVarUint(writer, EncodeDelta(arr[1], arr[0])); + + EdgeT curr = edges.front(); + curr.m_delta = EncodeDelta(arr[2], arr[1]); + + sort(edges.begin(), edges.end(), edge_less_p0()); + + stack st; + while (true) + { + CHECK_EQUAL ( curr.m_delta >> 62, 0, () ); + uint64_t delta = curr.m_delta << 2; + + // find next edges + int const nextNode = curr.m_p[1]; + vector::iterator i = lower_bound(edges.begin(), edges.end(), nextNode, edge_less_p0()); + bool const found = (i != edges.end() && i->m_p[0] == nextNode); + if (found) + { + // fill 2 tree-struct bites + ASSERT_NOT_EQUAL(i->m_side, -1, ()); + + // first child + delta |= (1 << i->m_side); + + vector::iterator j = i+1; + if (j != edges.end() && j->m_p[0] == nextNode) + { + // second child + ASSERT_EQUAL(i->m_side, 0, ()); + ASSERT_EQUAL(j->m_side, 1, ()); + + delta |= (1 << j->m_side); + + // push to stack for further processing + st.push(*j); + } + + curr = *i; + } + + // write delta for current element + WriteVarUint(writer, delta); + + if (!found) + { + // end of chain - pop current from stack or exit + if (st.empty()) + break; + else + { + curr = st.top(); + st.pop(); + } + } + } + } + + void DecodeTriangles(DeltasT const & deltas, + m2::PointU const & basePoint, + m2::PointU const & maxPoint, + PointsT & points) + { + size_t const count = deltas.size(); + ASSERT_GREATER ( count, 2, () ); + + points.reserve(3*count); + + points.push_back(DecodeDelta(deltas[0], basePoint)); + points.push_back(DecodeDelta(deltas[1], points.back())); + points.push_back(DecodeDelta(deltas[2] >> 2, points.back())); + + stack st; + + size_t ind = 2; + uint8_t treeBits = deltas[2] & 3; + + for (size_t i = 3; i < count;) + { + size_t trg[3]; + + if (treeBits & 1) + { + // common edge is 1->2 + trg[0] = ind; + trg[1] = ind-1; + trg[2] = ind-2; + + // push to stack for further processing + if (treeBits & 2) + st.push(ind); + } + else if (treeBits & 2) + { + // common edge is 2->0 + trg[0] = ind-2; + trg[1] = ind; + trg[2] = ind-1; + } + else + { + // end of chain - pop current from stack + ASSERT ( !st.empty(), () ); + ind = st.top(); + st.pop(); + treeBits = 2; + continue; + } + + // push points + points.push_back(points[trg[0]]); + points.push_back(points[trg[1]]); + points.push_back( DecodeDelta(deltas[i] >> 2, + PredictPointInTriangle(maxPoint, points[trg[0]], points[trg[1]], points[trg[2]]))); + + // next step + treeBits = deltas[i] & 3; + ind = points.size() - 1; + ++i; + } + + ASSERT ( treeBits == 0 && st.empty(), () ); + } } diff --git a/indexer/geometry_serialization.hpp b/indexer/geometry_serialization.hpp index 28608ddd89..c3698bf1f6 100644 --- a/indexer/geometry_serialization.hpp +++ b/indexer/geometry_serialization.hpp @@ -1,12 +1,17 @@ #pragma once #include "geometry_coding.hpp" +#include "tesselator_decl.hpp" #include "../geometry/point2d.hpp" +#include "../coding/reader.hpp" #include "../coding/writer.hpp" #include "../coding/varint.hpp" +#include "../std/algorithm.hpp" +#include "../std/bind.hpp" + #include "../base/buffer_vector.hpp" #include "../base/stl_add.hpp" @@ -20,35 +25,46 @@ namespace serial WriteVarUint(sink, v[i]); } - typedef void (*EncodeFunT)(vector const &, m2::PointU const &, m2::PointU const &, vector &); - typedef void (*DecodeFunT)(vector const &, m2::PointU const &, m2::PointU const &, vector &); + namespace pts { m2::PointU D2U(m2::PointD const & p); } - void Encode(EncodeFunT fn, vector const & points, int64_t base, vector & deltas); + typedef vector PointsT; + typedef vector DeltasT; + + typedef void (*EncodeFunT)(PointsT const &, m2::PointU const &, m2::PointU const &, DeltasT &); + typedef void (*DecodeFunT)(DeltasT const &, m2::PointU const &, m2::PointU const &, PointsT &); + + void Encode(EncodeFunT fn, vector const & points, int64_t base, DeltasT & deltas); typedef buffer_vector OutPointsT; - void Decode(DecodeFunT fn, vector const & deltas, int64_t base, OutPointsT & points); + void Decode(DecodeFunT fn, DeltasT const & deltas, int64_t base, OutPointsT & points); template void SaveInner(EncodeFunT fn, vector const & points, int64_t base, TSink & sink) { - vector deltas; + DeltasT deltas; Encode(fn, points, base, deltas); WriteVarUintArray(deltas, sink); } + template + void WriteBufferToSink(vector const & buffer, TSink & sink) + { + uint32_t const count = buffer.size(); + WriteVarUint(sink, count); + sink.Write(&buffer[0], count); + } + template void SaveOuter(EncodeFunT fn, vector const & points, int64_t base, TSink & sink) { - vector deltas; + DeltasT deltas; Encode(fn, points, base, deltas); vector buffer; MemWriter > writer(buffer); WriteVarUintArray(deltas, writer); - uint32_t const count = buffer.size(); - WriteVarUint(sink, count); - sink.Write(&buffer[0], count); + WriteBufferToSink(buffer, sink); } void const * LoadInner(DecodeFunT fn, void const * pBeg, size_t count, int64_t base, OutPointsT & points); @@ -61,10 +77,11 @@ namespace serial char * p = &buffer[0]; src.Read(p, count); - vector deltas; + DeltasT deltas; deltas.reserve(count / 2); ReadVarUint64Array(p, p + count, MakeBackInsertFunctor(deltas)); + points.reserve(deltas.size()); Decode(fn, deltas, base, points); } @@ -106,5 +123,47 @@ namespace serial { return LoadInner(&geo_coding::DecodeTriangleStrip, pBeg, count, base, points); } + + class TrianglesChainSaver + { + typedef m2::PointU PointT; + typedef tesselator::Edge EdgeT; + typedef vector BufferT; + + PointT m_base, m_max; + + list m_buffers; + + public: + TrianglesChainSaver(int64_t base); + + PointT GetBasePoint() const { return m_base; } + PointT GetMaxPoint() const { return m_max; } + + void operator() (PointT arr[3], vector edges); + + template void Save(TSink & sink) + { + size_t const count = m_buffers.size(); + CHECK_LESS(count, 256, ()); + WriteToSink(sink, static_cast(count)); + + for_each(m_buffers.begin(), m_buffers.end(), bind(&WriteBufferToSink, _1, ref(sink))); + } + }; + + void DecodeTriangles(DeltasT const & deltas, + m2::PointU const & basePoint, + m2::PointU const & maxPoint, + PointsT & triangles); + + template + void LoadOuterTriangles(TSource & src, int64_t base, OutPointsT & triangles) + { + int const count = ReadPrimitiveFromSource(src); + + for (int i = 0; i < count; ++i) + LoadOuter(&DecodeTriangles, src, base, triangles); + } //@} } diff --git a/indexer/indexer.pro b/indexer/indexer.pro index a9a929c265..d872803bc2 100644 --- a/indexer/indexer.pro +++ b/indexer/indexer.pro @@ -3,22 +3,12 @@ TARGET = indexer TEMPLATE = lib CONFIG += staticlib -#!macx:DEFINES += COMPILED_FROM_DSP # needed for Expat -#macx:DEFINES += HAVE_MEMMOVE # needed for Expat ROOT_DIR = .. DEPENDENCIES = geometry coding base expat include($$ROOT_DIR/common.pri) -!iphonesimulator-g++42 { - !iphonedevice-g++42 { - !bada-simulator { - PRE_TARGETDEPS += $$BINARIES_PATH/$${LIB_PREFIX}sgitess$$LIB_EXT - LIBS += -lsgitess - } - } -} SOURCES += \ osm2type.cpp \ @@ -39,7 +29,8 @@ SOURCES += \ data_header.cpp \ data_header_reader.cpp \ geometry_coding.cpp \ - geometry_serialization.cpp + geometry_serialization.cpp \ + tesselator.cpp \ HEADERS += \ feature.hpp \ @@ -71,5 +62,7 @@ HEADERS += \ tree_structure.hpp \ feature_impl.hpp \ geometry_coding.hpp \ - geometry_serialization.hpp \ - point_to_int64.hpp + geometry_serialization.hpp \ + point_to_int64.hpp \ + tesselator.hpp \ + tesselator_decl.hpp \ diff --git a/indexer/indexer_tests/indexer_tests.pro b/indexer/indexer_tests/indexer_tests.pro index e29803206e..063041ea54 100644 --- a/indexer/indexer_tests/indexer_tests.pro +++ b/indexer/indexer_tests/indexer_tests.pro @@ -37,5 +37,5 @@ SOURCES += \ data_header_test.cpp \ feature_bucketer_test.cpp \ feature_routine.cpp \ - geometry_coding_test.cpp - + geometry_coding_test.cpp \ + triangles_tree_coding_test.cpp diff --git a/indexer/indexer_tests/triangles_tree_coding_test.cpp b/indexer/indexer_tests/triangles_tree_coding_test.cpp new file mode 100644 index 0000000000..7f173259b8 --- /dev/null +++ b/indexer/indexer_tests/triangles_tree_coding_test.cpp @@ -0,0 +1,85 @@ +#include "../tesselator.hpp" +#include "../geometry_serialization.hpp" +#include "../mercator.hpp" + +#include "../../coding/reader.hpp" +#include "../../coding/writer.hpp" + +#include "../../testing/testing.hpp" + + +namespace +{ + typedef m2::PointD P; + + bool is_equal(P const & p1, P const & p2) + { + return p1.EqualDxDy(p2, MercatorBounds::GetCellID2PointAbsEpsilon()); + } + + bool FindTriangle(serial::OutPointsT const & test, P arr[]) + { + size_t const count = test.size(); + for (size_t i = 0; i < count; i+=3) + { + for (int base = 0; base < 3; ++base) + if (is_equal(test[i], arr[base]) && + is_equal(test[i+1], arr[(base+1)%3]) && + is_equal(test[i+2], arr[(base+2)%3])) + { + return true; + } + } + return false; + } + + void CompareTriangles(serial::OutPointsT const & test, + P arrP[], uintptr_t arrT[][3], size_t count) + { + TEST_EQUAL(test.size(), 3*count, (test)); + + for (size_t i = 0; i < count; ++i) + { + P trg[] = { arrP[arrT[i][0]], arrP[arrT[i][1]], arrP[arrT[i][2]] }; + TEST ( FindTriangle(test, trg), ("Triangles : ", test, " Etalon : ", trg[0], trg[1], trg[2]) ); + } + } + + void TestTrianglesCoding(P arrP[], size_t countP, uintptr_t arrT[][3], size_t countT) + { + tesselator::TrianglesInfo info; + info.AssignPoints(arrP, arrP + countP); + + info.Reserve(countT); + + for (size_t i = 0; i < countT; ++i) + info.Add(arrT[i]); + + serial::TrianglesChainSaver saver(0); + info.ProcessPortions(saver.GetBasePoint(), saver.GetMaxPoint(), &serial::pts::D2U, saver); + + vector buffer; + MemWriter > writer(buffer); + saver.Save(writer); + + TEST ( !buffer.empty(), () ); + + MemReader reader(&buffer[0], buffer.size()); + ReaderSource src(reader); + + serial::OutPointsT triangles; + serial::LoadOuterTriangles(src, 0, triangles); + + CompareTriangles(triangles, arrP, arrT, countT); + } +} + +UNIT_TEST(TrianglesCoding_Smoke) +{ + { + P arrP[] = { P(0, 0), P(0, 1), P(1, 0), P(1, 1), P(0, -1), P(-1, 0) }; + uintptr_t arrT[][3] = { {0, 1, 2}, {1, 3, 2}, {4, 0, 2}, {1, 0, 5}, {4, 5, 0} }; + + TestTrianglesCoding(arrP, ARRAY_SIZE(arrP), arrT, ARRAY_SIZE(arrT)); + } +} diff --git a/indexer/indexer_tool/feature_sorter.cpp b/indexer/indexer_tool/feature_sorter.cpp index 09a9ab7d97..4649de59cd 100644 --- a/indexer/indexer_tool/feature_sorter.cpp +++ b/indexer/indexer_tool/feature_sorter.cpp @@ -7,11 +7,10 @@ #include "../../indexer/feature_processor.hpp" #include "../../indexer/feature_visibility.hpp" #include "../../indexer/feature_impl.hpp" -#include "../../indexer/cell_id.hpp" #include "../../indexer/geometry_serialization.hpp" +#include "../../indexer/tesselator.hpp" #include "../../geometry/polygon.hpp" -#include "../../geometry/tesselator.hpp" #include "../../platform/platform.hpp" @@ -135,6 +134,10 @@ namespace feature m_writer.Finish(); } + private: + typedef vector points_t; + typedef list holes_t; + class GeometryHolder { public: @@ -144,8 +147,6 @@ namespace feature FeaturesCollector2 & m_rMain; FeatureBuilder2 & m_rFB; - typedef vector points_t; - points_t m_current; int64_t m_base; @@ -157,11 +158,21 @@ namespace feature serial::SaveOuterPath(points, m_base, *m_rMain.m_geoFile[i]); } - void WriteOuterTriangles(points_t const & triangles, int i) + void WriteOuterTriangles(points_t const & bound, holes_t const & holes, int i) { m_buffer.m_trgMask |= (1 << i); m_buffer.m_trgOffset.push_back(m_rMain.GetFileSize(*m_rMain.m_trgFile[i])); - feature::SaveTriangles(triangles, m_base, *m_rMain.m_trgFile[i]); + + // tesselation + tesselator::TrianglesInfo info; + tesselator::TesselateInterior(bound, holes, info); + + // triangles processing + serial::TrianglesChainSaver saver(m_base); + info.ProcessPortions(saver.GetBasePoint(), saver.GetMaxPoint(), &serial::pts::D2U, saver); + + // saving to file + saver.Save(*m_rMain.m_trgFile[i]); } void FillInnerPointsMask(points_t const & points, uint32_t scaleIndex) @@ -279,15 +290,16 @@ namespace feature return true; } - void AddTriangles(points_t const & triangles, int scaleIndex) + void AddTriangles(points_t const & bound, holes_t const & holes, int scaleIndex) { ASSERT ( m_buffer.m_innerTrg.empty(), () ); m_trgInner = false; - WriteOuterTriangles(triangles, scaleIndex); + WriteOuterTriangles(bound, holes, scaleIndex); } }; + public: void operator() (FeatureBuilder2 & fb) { (void)GetFileSize(m_datFile); @@ -302,7 +314,7 @@ namespace feature if (fb.IsDrawableInRange(i > 0 ? g_arrScales[i-1] + 1 : 0, g_arrScales[i])) { // simplify and serialize geometry - tesselator::points_container points; + points_t points; SimplifyPoints(holder.GetSourcePoints(), points, g_arrScales[i]); if (isLine) @@ -312,15 +324,15 @@ namespace feature { // simplify and serialize triangles - tesselator::holes_container const & holes = fb.GetHoles(); + holes_t const & holes = fb.GetHoles(); if (holes.empty() && holder.TryToMakeStrip(points)) continue; - tesselator::holes_container simpleHoles; - for (tesselator::holes_container::const_iterator iH = holes.begin(); iH != holes.end(); ++iH) + holes_t simpleHoles; + for (holes_t::const_iterator iH = holes.begin(); iH != holes.end(); ++iH) { - simpleHoles.push_back(tesselator::points_container()); + simpleHoles.push_back(points_t()); SimplifyPoints(*iH, simpleHoles.back(), g_arrScales[i]); @@ -328,11 +340,7 @@ namespace feature simpleHoles.pop_back(); } - tesselator::points_container triangles; - tesselator::TesselateInterior(points, simpleHoles, triangles); - - if (!triangles.empty()) - holder.AddTriangles(triangles, i); + holder.AddTriangles(points, simpleHoles, i); } } } diff --git a/indexer/tesselator.cpp b/indexer/tesselator.cpp new file mode 100644 index 0000000000..e9526090c5 --- /dev/null +++ b/indexer/tesselator.cpp @@ -0,0 +1,231 @@ +#include "tesselator.hpp" +#include "geometry_coding.hpp" + +#include "../base/assert.hpp" +#include "../base/logging.hpp" + +#include "../std/queue.hpp" + +#include "../../3party/sgitess/interface.h" + +#include "../base/start_mem_debug.hpp" + + +namespace tesselator +{ + struct AddTessPointF + { + tess::Tesselator & m_tess; + AddTessPointF(tess::Tesselator & tess) : m_tess(tess) + {} + void operator()(m2::PointD const & p) + { + m_tess.add(tess::Vertex(p.x, p.y)); + } + }; + + void TesselateInterior(PointsT const & bound, HolesT const & holes, TrianglesInfo & info) + { + tess::VectorDispatcher disp; + tess::Tesselator tess; + tess.setDispatcher(&disp); + tess.setWindingRule(tess::WindingOdd); + + tess.beginPolygon(); + + tess.beginContour(); + for_each(bound.begin(), bound.end(), AddTessPointF(tess)); + tess.endContour(); + + for (HolesT::const_iterator it = holes.begin(); it != holes.end(); ++it) + { + tess.beginContour(); + for_each(it->begin(), it->end(), AddTessPointF(tess)); + tess.endContour(); + } + + tess.endPolygon(); + + // assign points + vector const & vert = disp.vertices(); + info.AssignPoints(vert.begin(), vert.end()); + + for (size_t i = 0; i < disp.indices().size(); ++i) + { + if (disp.indices()[i].first != tess::TrianglesList) + { + LOG(LERROR, ("We've got invalid type during teselation:", disp.indices()[i].first)); + continue; + } + + vector const & indices = disp.indices()[i].second; + size_t const count = indices.size(); + ASSERT_GREATER(count, 0, ()); + ASSERT_EQUAL(count % 3, 0, ()); + + info.Reserve(count / 3); + for (size_t j = 0; j < count; j += 3) + { + ASSERT_LESS ( j+2, count, () ); + info.Add(&indices[j]); + } + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // TrianglesInfo::ListInfo implementation + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int TrianglesInfo::ListInfo::empty_key = -1; + + void TrianglesInfo::ListInfo::AddNeighbour(int p1, int p2, int trg) + { + // find or insert element for key + pair ret = m_neighbours.insert(make_pair(make_pair(p1, p2), trg)); + + // triangles should not duplicate + CHECK ( ret.second, ("Duplicating triangles for indices : ", p1, p2) ); + } + + void TrianglesInfo::ListInfo::Add(uintptr_t const * arr) + { + int arr32[] = { arr[0], arr[1], arr[2] }; + m_triangles.push_back(Triangle(arr32)); + + size_t const trg = m_triangles.size()-1; + for (int i = 0; i < 3; ++i) + AddNeighbour(arr32[i], arr32[(i+1)%3], trg); + } + + /// Find best (cheap in serialization) start edge for processing. + TrianglesInfo::ListInfo::iter_t + TrianglesInfo::ListInfo::FindStartTriangle() const + { + for (iter_t i = m_neighbours.begin(); i != m_neighbours.end(); ++i) + { + if (m_neighbours.find(make_pair(i->first.second, i->first.first)) == m_neighbours.end()) + return i; + } + + ASSERT ( false, ("?WTF? There is no border triangles!") ); + return m_neighbours.end(); + } + + /// Return indexes of common edges of [to, from] triangles. + pair CommonEdge(Triangle const & to, Triangle const & from) + { + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + if (to.m_p[i] == from.m_p[my::NextModN(j, 3)] && to.m_p[my::NextModN(i, 3)] == from.m_p[j]) + return make_pair(i, j); + } + } + + ASSERT ( false, ("?WTF? Triangles not neighbours!") ); + return make_pair(-1, -1); + } + + /// Get neighbours of 'trg' triangle, wich was riched from 'from' triangle. + /// @param[out] nb Neighbours indexes of 'trg' if 0->1 is common edge with'from': + /// - nb[0] - by 1->2 edge; + /// - nb[1] - by 2->0 edge; + void TrianglesInfo::ListInfo::GetNeighbours( + Triangle const & trg, Triangle const & from, int * nb) const + { + int i = my::NextModN(CommonEdge(trg, from).first, 3); + int j = my::NextModN(i, 3); + + int ind = 0; + iter_t it = m_neighbours.find(make_pair(trg.m_p[j], trg.m_p[i])); + nb[ind++] = (it != m_neighbours.end()) ? it->second : empty_key; + + it = m_neighbours.find(make_pair(trg.m_p[my::NextModN(j, 3)], trg.m_p[j])); + nb[ind++] = (it != m_neighbours.end()) ? it->second : empty_key; + } + + /// Calc delta of 'from'->'to' graph edge. + uint64_t TrianglesInfo::ListInfo::CalcDelta( + PointsInfo const & points, Triangle const & from, Triangle const & to) const + { + pair const p = CommonEdge(to, from); + + m2::PointU const prediction = + PredictPointInTriangle(points.m_max, + // common edge with 'to' + points.m_points[from.m_p[(p.second+1) % 3]], + points.m_points[from.m_p[(p.second)]], + // diagonal point of 'from' + points.m_points[from.m_p[(p.second+2) % 3]]); + + // delta from prediction to diagonal point of 'to' + return EncodeDelta(points.m_points[to.m_p[(p.first+2) % 3]], prediction); + } + + // Element with less m_delta is better than another one. + struct edge_greater_delta + { + bool operator() (Edge const & e1, Edge const & e2) const + { + return (e1.m_delta > e2.m_delta); + } + }; + + void TrianglesInfo::ListInfo::MakeTrianglesChain( + PointsInfo const & points, iter_t start, vector & chain) const + { + Triangle const fictive(start->first.second, start->first.first, -1); + + priority_queue, edge_greater_delta> q; + q.push(Edge(-1, start->second, 0.0, -1)); + + // marks of visited nodes + vector visited; + visited.resize(m_triangles.size()); + + while (!q.empty()) + { + // pop current element + Edge e = q.top(); + q.pop(); + + // check if already processed + if (visited[e.m_p[1]]) + continue; + visited[e.m_p[1]] = true; + + // push to chain + chain.push_back(e); + + Triangle const & trg = m_triangles[e.m_p[1]]; + + // get neighbours + int nb[2]; + GetNeighbours(trg, (e.m_p[0] == -1) ? fictive : m_triangles[e.m_p[0]], nb); + + // push neighbours to queue + for (int i = 0; i < 2; ++i) + if (nb[i] != empty_key && !visited[nb[i]]) + q.push(Edge(e.m_p[1], nb[i], CalcDelta(points, trg, m_triangles[nb[i]]), i)); + } + } + + void TrianglesInfo::Add(uintptr_t const * arr) + { + m2::PointD arrP[] = { m_points[arr[0]], m_points[arr[1]], m_points[arr[2]] }; + double const cp = m2::CrossProduct(arrP[1] - arrP[0], arrP[2] - arrP[1]); + + if (fabs(cp) > 1.0E-4) + { + bool const isCCW = (cp > 0.0); + + if (m_isCCW == 0) + m_isCCW = (isCCW ? 1 : -1); + else + CHECK_EQUAL ( m_isCCW == 1, isCCW, () ); + } + + m_triangles.back().Add(arr); + } +} diff --git a/indexer/tesselator.hpp b/indexer/tesselator.hpp new file mode 100644 index 0000000000..fcd821cad0 --- /dev/null +++ b/indexer/tesselator.hpp @@ -0,0 +1,153 @@ +#pragma once +#include "tesselator_decl.hpp" + +#include "../geometry/point2d.hpp" + +#include "../std/list.hpp" +#include "../std/vector.hpp" +#include "../std/unordered_map.hpp" +#include "../std/iterator.hpp" + + +namespace tesselator +{ + typedef vector PointsT; + typedef list HolesT; + + struct Triangle + { + int m_p[3]; + + Triangle(int p0, int p1, int p2) + { + m_p[0] = p0; + m_p[1] = p1; + m_p[2] = p2; + } + + Triangle(int const * p) + { + for (int i = 0; i < 3; ++i) + m_p[i] = p[i]; + } + + int GetPoint3(pair const & p) const + { + for (int i = 0; i < 3; ++i) + if (m_p[i] != p.first && m_p[i] != p.second) + return m_p[i]; + + ASSERT ( false, ("Triangle with equal points") ); + return -1; + } + }; + + // Converted points, prepared for serialization. + struct PointsInfo + { + typedef m2::PointU PointT; + vector m_points; + PointT m_base, m_max; + + PointsInfo(PointT const & baseP, PointT const & maxP) + : m_base(baseP), m_max(maxP) + { + } + }; + + class TrianglesInfo + { + PointsT m_points; + + class ListInfo + { + static int empty_key; + + vector m_triangles; + + // directed edge -> triangle + typedef unordered_map, int> neighbours_t; + neighbours_t m_neighbours; + + void AddNeighbour(int p1, int p2, int trg); + + void GetNeighbours( + Triangle const & trg, Triangle const & from, int * nb) const; + + uint64_t CalcDelta( + PointsInfo const & points, Triangle const & from, Triangle const & to) const; + + public: + typedef neighbours_t::const_iterator iter_t; + + ListInfo(size_t count) + { + m_triangles.reserve(count); + } + + void Add(uintptr_t const * arr); + + iter_t FindStartTriangle() const; + + void MakeTrianglesChain(PointsInfo const & points, iter_t start, vector & chain) const; + + Triangle GetTriangle(int i) const { return m_triangles[i]; } + }; + + list m_triangles; + + int m_isCCW; // 0 - uninitialized; -1 - false; 1 - true + + public: + TrianglesInfo() : m_isCCW(0) {} + + /// @name Making functions. + //@{ + template void AssignPoints(IterT b, IterT e) + { + m_points.reserve(distance(b, e)); + while (b != e) + { + m_points.push_back(m2::PointD(b->x, b->y)); + ++b; + } + } + + void Reserve(size_t count) { m_triangles.push_back(ListInfo(count)); } + + void Add(uintptr_t const * arr); + //@{ + + /// Triangles chains processing function. + template + void ProcessPortions(m2::PointU const & baseP, m2::PointU const & maxP, + m2::PointU (*convert) (m2::PointD const &), EmitterT & emitter) + { + // convert points from double to uint + size_t const count = m_points.size(); + PointsInfo points(baseP, maxP); + points.m_points.reserve(count); + for (size_t i = 0; i < count; ++i) + points.m_points.push_back((*convert)(m_points[i])); + + // process portions and push out result chains + vector chain; + for (list::const_iterator i = m_triangles.begin(); i != m_triangles.end(); ++i) + { + chain.clear(); + typename ListInfo::iter_t start = i->FindStartTriangle(); + i->MakeTrianglesChain(points, start, chain); + + Triangle const trg = i->GetTriangle(start->second); + m2::PointU arr[] = { points.m_points[start->first.first], + points.m_points[start->first.second], + points.m_points[trg.GetPoint3(start->first)] }; + + emitter(arr, chain); + } + } + }; + + /// Main tesselate function. + void TesselateInterior(PointsT const & bound, HolesT const & holes, TrianglesInfo & info); +} diff --git a/indexer/tesselator_decl.hpp b/indexer/tesselator_decl.hpp new file mode 100644 index 0000000000..5f0cd5a1a6 --- /dev/null +++ b/indexer/tesselator_decl.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "../base/assert.hpp" + +namespace tesselator +{ + // Edge of graph, builded from triangles list. + struct Edge + { + int m_p[2]; // indexes of connected triangles (0 -> 1) + uint64_t m_delta; // delta of 1 - triangle from 0 - triangle + + // intersected rib of 0 - triangle: + // - -1 - uninitialized or root edge + // - 0 - this edge intersects 1-2 rib; + // - 1 - this edge intersects 2-0 rib; + char m_side; + + Edge(int from, int to, uint64_t delta, char side) + : m_delta(delta), m_side(side) + { + m_p[0] = from; + m_p[1] = to; + } + }; +}