diff --git a/geometry/geometry_tests/point_test.cpp b/geometry/geometry_tests/point_test.cpp index 609ce8c84d..71eaa24964 100644 --- a/geometry/geometry_tests/point_test.cpp +++ b/geometry/geometry_tests/point_test.cpp @@ -57,3 +57,31 @@ UNIT_TEST(PointInTriangle_EmptyTriangle) m2::PointD pt(27, 31); TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 16), pt, pt, pt), ()); } + +/// @todo add more tests +UNIT_TEST(ArrowPoints) +{ + m2::PointF p1, p2, p3; + + m2::ArrowPoints(m2::PointF(0, 0), m2::PointF(1, 0), 1.f, 1.f, p1, p2, p3); + TEST(m2::AlmostEqual(p1, m2::PointF(1.f, 1.f)), ()); + TEST(m2::AlmostEqual(p2, m2::PointF(2.f, 0.f)), ()); + TEST(m2::AlmostEqual(p3, m2::PointF(1.f, -1.f)), ()); + + m2::PointD d1, d2, d3; + m2::ArrowPoints(m2::PointD(-1., 2.), m2::PointD(-1., 100.), 2., 5., d1, d2, d3); + TEST(m2::AlmostEqual(d1, m2::PointD(-3.f, 100.f)), ()); + TEST(m2::AlmostEqual(d2, m2::PointD(-1.f, 105.f)), ()); + TEST(m2::AlmostEqual(d3, m2::PointD(1.f, 100.f)), ()); +} + +UNIT_TEST(PointAtSegment) +{ + m2::PointF p1, p2, p3; + + TEST(m2::AlmostEqual(m2::PointAtSegment(m2::PointF(0, 0), m2::PointF(1, 0), 0.5f), m2::PointF(0.5f, 0.f)), ()); + TEST(m2::AlmostEqual(m2::PointAtSegment(m2::PointF(0, 0), m2::PointF(0, 1), 0.3f), m2::PointF(0.f, 0.3f)), ()); + TEST(m2::AlmostEqual(m2::PointAtSegment(m2::PointD(0., 0.), m2::PointD(30., 40.), 5.), m2::PointD(3., 4.)), ()); + TEST(m2::AlmostEqual(m2::PointAtSegment(m2::PointF(-3, -4), m2::PointF(-30, -40), 5.f), m2::PointF(-6.f, -8.f)), ()); + TEST(m2::AlmostEqual(m2::PointAtSegment(m2::PointD(14., -48.), m2::PointD(70., -240.), 25.), m2::PointD(21., -72.)), ()); +} diff --git a/geometry/point2d.hpp b/geometry/point2d.hpp index c75ca2667e..f37195b4bf 100644 --- a/geometry/point2d.hpp +++ b/geometry/point2d.hpp @@ -142,6 +142,14 @@ namespace m2 double const module = this->Length(); return Point(x / module, y / module); } + + pair, Point > Normals(T prolongationFactor = 1) const + { + T const prolongatedX = prolongationFactor * x; + T const prolongatedY = prolongationFactor * y; + return pair, Point >(Point(static_cast(-prolongatedY), static_cast(prolongatedX)), + Point(static_cast(prolongatedY), static_cast(-prolongatedX))); + } // @} m2::Point const & operator *= (math::Matrix const & m) @@ -311,6 +319,33 @@ namespace m2 return my::AlmostEqual(a.x, b.x, maxULPs) && my::AlmostEqual(a.y, b.y, maxULPs); } + /// Calculate three point of a triangle (p1, p2 and p3) which gives a arrow at the end of segment s, f + /// with respect to w - arrow's width and l - arrow's length + template + void ArrowPoints(Point const & b, Point const & e, T w, T l, + Point & p1, Point & p2, Point & p3) + { + ASSERT(!m2::AlmostEqual(b, e), ()); + Point const beVec = e - b; + Point beNormalizedVec = beVec.Normalize(); + pair, Point > beNormVecs = beNormalizedVec.Normals(w); + + p1 = e + beNormVecs.first; + p2 = e + beNormalizedVec * l; + p3 = e + beNormVecs.second; + } + + /// Returns a point which is belonged to the segment p1, p2 with respet the indent shiftFromP1 from p1. + /// If shiftFromP1 is more the distance between (p1, p2) it returns p2. + /// If shiftFromP1 is less or equal zero it returns p1. + template + Point PointAtSegment(Point const & p1, Point const & p2, T shiftFromP1) + { + Point p12 = p2 - p1; + shiftFromP1 = my::clamp(shiftFromP1, 0.0, p12.Length()); + return p1 + p12.Normalize() * shiftFromP1; + } + template TArchive & operator >> (TArchive & ar, m2::Point & pt) { diff --git a/graphics/depth_constants.hpp b/graphics/depth_constants.hpp index 9a329c88cc..d446c6189c 100644 --- a/graphics/depth_constants.hpp +++ b/graphics/depth_constants.hpp @@ -24,6 +24,7 @@ namespace graphics static const int activePinDepth = routingFinishDepth - balloonContentInc; static const int routingSymbolsDepth = activePinDepth - balloonContentInc; static const int tracksDepth = routingSymbolsDepth - balloonContentInc; + static const int arrowDepth = tracksDepth + 10; static const int tracksOutlineDepth = tracksDepth - 10; static const int bookmarkDepth = tracksOutlineDepth - balloonContentInc; } diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index a28e6e2efe..9970ef0468 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -249,7 +249,7 @@ size_t BookmarkManager::CreateBmCategory(string const & name) return (m_categories.size()-1); } -void BookmarkManager::DrawItems(shared_ptr const & e) const +void BookmarkManager::DrawItems(shared_ptr const & e, int drawScale, double visualScale) const { #ifndef USE_DRAPE ASSERT(m_cache != NULL, ()); @@ -258,12 +258,12 @@ void BookmarkManager::DrawItems(shared_ptr const & e) const LazyMatrixCalc matrix(screen, m_lastScale); - auto trackUpdateFn = [&matrix, &limitRect, this](Track const * track) + auto trackUpdateFn = [&matrix, &limitRect, this, drawScale, visualScale](Track const * track) { if (limitRect.IsIntersect(track->GetLimitRect())) { if (!track->HasDisplayList() || matrix.IsScaleChanged()) - track->CreateDisplayList(m_bmScreen, matrix.GetScaleG2P()); + track->CreateDisplayList(m_bmScreen, matrix.GetScaleG2P(), drawScale, visualScale); } else track->DeleteDisplayList(); diff --git a/map/bookmark_manager.hpp b/map/bookmark_manager.hpp index 72d6e8b86a..6b366ff517 100644 --- a/map/bookmark_manager.hpp +++ b/map/bookmark_manager.hpp @@ -55,7 +55,7 @@ public: BookmarkCategory * GetBmCategory(size_t index) const; size_t CreateBmCategory(string const & name); - void DrawItems(shared_ptr const & e) const; + void DrawItems(shared_ptr const & e, int drawScale, double visualScale) const; /// @name Delete bookmarks category with all bookmarks. /// @return true if category was deleted diff --git a/map/framework.cpp b/map/framework.cpp index 7fcff9aac8..88b876a401 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -768,7 +768,7 @@ void Framework::DrawAdditionalInfo(shared_ptr const & e) pScreen->endFrame(); - m_bmManager.DrawItems(e); + m_bmManager.DrawItems(e, GetDrawScale(), m_guiController->GetVisualScale()); m_guiController->UpdateElements(); m_guiController->DrawFrame(pScreen); } @@ -2000,6 +2000,7 @@ void Framework::InsertRoute(Route const & route) Track track(route.GetPoly()); track.SetName(route.GetName()); + track.SetTurnsGeometry(route.GetTurnsGeometry()); Track::TrackOutline outlines[] { diff --git a/map/map_tests/tracks_tests.cpp b/map/map_tests/tracks_tests.cpp index e69de29bb2..6364321d31 100644 --- a/map/map_tests/tracks_tests.cpp +++ b/map/map_tests/tracks_tests.cpp @@ -0,0 +1,34 @@ +#include "../../testing/testing.hpp" + +#include "../track.hpp" + +UNIT_TEST(clipArrowBodyAndGetArrowDirection) +{ + vector ptsTurn = {m2::PointD(4452766.0004956936, -8008660.158053305), + m2::PointD(4452767.909028396, -8008670.0188056063), + m2::PointD(4452768.5452059628, -8008681.4700018261), + m2::PointD(4452765.6824069088, -8008693.8754644003), + m2::PointD(4452759.3206312312, -8008705.6447494039), + m2::PointD(4452746.2789910901, -8008720.9130110294), + m2::PointD(4452746.2789910901, -8008720.9130110294), + m2::PointD(4452670.8919493081, -8008780.7137024049), + m2::PointD(4452631.7670288859, -8008811.5683144433), + m2::PointD(4452567.5130945379, -8008863.0986974351)}; + pair arrowDirection; + bool result = clipArrowBodyAndGetArrowDirection(ptsTurn, arrowDirection, 5, 13., 13., 19.); + + TEST(result, ()); + + TEST(m2::AlmostEqual(arrowDirection.first, m2::PointD(4452740.7948958352, -8008725.2632638067)), ()); + TEST(m2::AlmostEqual(arrowDirection.second, m2::PointD(4452736.0942427581, -8008728.9920519013)), ()); + + TEST_EQUAL(ptsTurn.size(), 4, ()); + if (ptsTurn.size() == 4) + { + TEST(m2::AlmostEqual(ptsTurn[0], m2::PointD(4452754.7223071428, -8008711.0281532137)), ()); + TEST(m2::AlmostEqual(ptsTurn[1], m2::PointD(4452746.2789910901, -8008720.9130110294)), ()); + TEST(m2::AlmostEqual(ptsTurn[2], m2::PointD(4452746.2789910901, -8008720.9130110294)), ()); + TEST(m2::AlmostEqual(ptsTurn[3], m2::PointD(4452736.0942427581, -8008728.9920519013)), ()); + } + +} diff --git a/map/track.cpp b/map/track.cpp index a42f10310b..d8e5fa6063 100644 --- a/map/track.cpp +++ b/map/track.cpp @@ -6,6 +6,7 @@ #include "../graphics/pen.hpp" #include "../graphics/depth_constants.hpp" #include "../graphics/display_list.hpp" +#include "../graphics/defines.hpp" #include "../geometry/distance.hpp" #include "../geometry/simplification.hpp" @@ -14,6 +15,128 @@ #include "../base/timer.hpp" #include "../base/logging.hpp" +#include "../indexer/scales.hpp" + +typedef buffer_vector PointContainerT; + +pair shiftArrow(pair const & arrowDirection) +{ + return pair(arrowDirection.first - (arrowDirection.second - arrowDirection.first), + arrowDirection.first); +} + +bool clipArrowBodyAndGetArrowDirection(vector & ptsTurn, pair & arrowDirection, + size_t turnIndex, double beforeTurn, double afterTurn, double arrowLength) +{ + size_t const ptsTurnSz = ptsTurn.size(); + ASSERT(turnIndex < ptsTurnSz, ()); + + /// Clipping after turnIndex + size_t i = turnIndex; + double len = 0, vLen = 0; + while (len < afterTurn) + { + if (i >= ptsTurnSz - 2) + return false; + vLen = ptsTurn[i + 1].Length(ptsTurn[i]); + len += vLen; + i += 1; + } + if (my::AlmostEqual(vLen, 0.)) + return false; + + double lenForArrow = len - afterTurn; + double vLenForArrow = lenForArrow; + size_t j = i; + while (lenForArrow < arrowLength) + { + if (j >= ptsTurnSz - 2) + return false; + vLenForArrow = ptsTurn[j + 1].Length(ptsTurn[j]); + lenForArrow += vLenForArrow; + j += 1; + } + if (m2::AlmostEqual(ptsTurn[j - 1], ptsTurn[j])) + return false; + m2::PointD arrowEnd = m2::PointAtSegment(ptsTurn[j - 1], ptsTurn[j], vLenForArrow - (lenForArrow - arrowLength)); + + if (my::AlmostEqual(len, afterTurn)) + ptsTurn.resize(i + 1); + else + { + if (!m2::AlmostEqual(ptsTurn[i], ptsTurn[i - 1])) + { + m2::PointD const p = m2::PointAtSegment(ptsTurn[i - 1], ptsTurn[i], vLen - (len - afterTurn)); + ptsTurn[i] = p; + ptsTurn.resize(i + 1); + } + else + ptsTurn.resize(i); + } + + // Calculating arrow direction + arrowDirection.first = ptsTurn.back(); + arrowDirection.second = arrowEnd; + arrowDirection = shiftArrow(arrowDirection); + + /// Clipping before turnIndex + i = turnIndex; + len = 0; + while (len < beforeTurn) + { + if (i <= 1) + return false; + vLen = ptsTurn[i - 1].Length(ptsTurn[i]); + len += vLen; + i -= 1; + } + if (my::AlmostEqual(vLen, 0.)) + return false; + + if (my::AlmostEqual(len, beforeTurn)) + { + if (i != 0) + { + ptsTurn.erase(ptsTurn.begin(), ptsTurn.begin() + i); + return true; + } + } + else + { + if (!m2::AlmostEqual(ptsTurn[i], ptsTurn[i + 1])) + { + m2::PointD const p = m2::PointAtSegment(ptsTurn[i + 1], ptsTurn[i], vLen - (len - beforeTurn)); + ptsTurn[i] = p; + if (i != 0) + { + ptsTurn.erase(ptsTurn.begin(), ptsTurn.begin() + i); + return true; + } + } + else + { + ptsTurn.erase(ptsTurn.begin(), ptsTurn.begin() + i); + return true; + } + } + return true; +} + +void drawArrowTriangle(graphics::Screen * dlScreen, pair const & arrowDirection, + double arrowWidth, double arrowLength, graphics::Color arrowColor, double arrowDepth) +{ + ASSERT(dlScreen, ()); + m2::PointD p1, p2, p3; + + m2::ArrowPoints(arrowDirection.first, arrowDirection.second, arrowWidth, arrowLength, p1, p2, p3); + vector arrow; + arrow.reserve(3); + arrow.push_back(p1); + arrow.push_back(p2); + arrow.push_back(p3); + + dlScreen->drawConvexPolygon(&arrow[0], arrow.size(), arrowColor, arrowDepth); +} Track::~Track() { @@ -84,7 +207,41 @@ public: } -void Track::CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matrix) const +void Track::CreateDisplayListArrows(graphics::Screen * dlScreen, MatrixT const & matrix, double visualScale) const +{ + double const beforeTurn = 13. * visualScale; + double const afterTurn = 13. * visualScale; + double const arrowWidth = 10. * visualScale; + double const arrowLength = 19. * visualScale; + double const arrowBodyWidth = 8. * visualScale; + graphics::Color const arrowColor(graphics::Color(0, 0, 128, 255)); + double const arrowDepth = graphics::arrowDepth; + + pair arrowDirection; + vector ptsTurn; + ptsTurn.reserve(m_turnsGeom.size()); + for (routing::turns::TurnGeom const & t : m_turnsGeom) + { + ptsTurn.clear(); + if (t.m_points.empty()) + continue; + transform(t.m_points.begin(), t.m_points.end(), back_inserter(ptsTurn), DoLeftProduct(matrix)); + + if (!clipArrowBodyAndGetArrowDirection(ptsTurn, arrowDirection, t.m_turnIndex, beforeTurn, afterTurn, arrowLength)) + continue; + size_t const ptsTurnSz = ptsTurn.size(); + if (ptsTurnSz < 2) + continue; + + graphics::Pen::Info const outlineInfo(arrowColor, arrowBodyWidth); + uint32_t const outlineId = dlScreen->mapInfo(outlineInfo); + dlScreen->drawPath(&ptsTurn[0], ptsTurnSz, 0, outlineId, arrowDepth); + + drawArrowTriangle(dlScreen, arrowDirection, arrowWidth, arrowLength, arrowColor, arrowDepth); + } +} + +void Track::CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matrix, int drawScale, double visualScale) const { DeleteDisplayList(); @@ -93,7 +250,6 @@ void Track::CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matri dlScreen->beginFrame(); dlScreen->setDisplayList(m_dList); - typedef buffer_vector PointContainerT; size_t const count = m_polyline.GetSize(); PointContainerT pts1(count); @@ -101,16 +257,16 @@ void Track::CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matri PointContainerT pts2; pts2.reserve(count); - SimplifyDP(pts1.begin(), pts1.end(), math::sqr(GetMainWidth()), + SimplifyDP(pts1.begin(), pts1.end(), GetMainWidth(), m2::DistanceToLineSquare(), MakeBackInsertFunctor(pts2)); - double baseDepth = graphics::tracksDepth - 10 * m_outlines.size(); + double baseDepthTrack = graphics::tracksDepth - 10 * m_outlines.size(); for (TrackOutline const & outline : m_outlines) { graphics::Pen::Info const outlineInfo(outline.m_color, outline.m_lineWidth); uint32_t const outlineId = dlScreen->mapInfo(outlineInfo); - dlScreen->drawPath(pts2.data(), pts2.size(), 0, outlineId, baseDepth); - baseDepth += 10; + dlScreen->drawPath(pts2.data(), pts2.size(), 0, outlineId, baseDepthTrack); + baseDepthTrack += 10; } if (!m_beginSymbols.empty() || !m_endSymbols.empty()) @@ -127,6 +283,9 @@ void Track::CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matri for_each(m_endSymbols.begin(), m_endSymbols.end(), symDrawer); } + if (drawScale >= scales::GetNavigationScale()) + CreateDisplayListArrows(dlScreen, matrix, visualScale); + dlScreen->setDisplayList(0); dlScreen->endFrame(); } @@ -160,6 +319,7 @@ void Track::Swap(Track & rhs) m_name.swap(rhs.m_name); m_polyline.Swap(rhs.m_polyline); + m_turnsGeom.swap(rhs.m_turnsGeom); DeleteDisplayList(); rhs.DeleteDisplayList(); diff --git a/map/track.hpp b/map/track.hpp index 955df8c20d..8fc5099f4d 100644 --- a/map/track.hpp +++ b/map/track.hpp @@ -6,6 +6,8 @@ #include "../graphics/color.hpp" #include "../graphics/defines.hpp" +#include "../routing/turns.hpp" + #include "../std/noncopyable.hpp" class Navigator; @@ -40,7 +42,7 @@ public: graphics::Color const & GetMainColor() const; void Draw(graphics::Screen * pScreen, MatrixT const & matrix) const; - void CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matrix) const; + void CreateDisplayList(graphics::Screen * dlScreen, MatrixT const & matrix, int drawScale, double visualScale) const; void DeleteDisplayList() const; bool HasDisplayList() const { return m_dList != nullptr; } @@ -59,6 +61,7 @@ public: string const & GetName() const { return m_name; } void SetName(string const & name) { m_name = name; } + void SetTurnsGeometry(routing::turns::TurnsGeomT const & turnsGeom) { m_turnsGeom = turnsGeom; } PolylineD const & GetPolyline() const { return m_polyline; } m2::RectD const & GetLimitRect() const { return m_rect; } @@ -72,6 +75,8 @@ public: void Swap(Track & rhs); private: + void CreateDisplayListArrows(graphics::Screen * dlScreen, MatrixT const & matrix, double visualScale) const; + bool m_isVisible = false; string m_name; @@ -90,7 +95,11 @@ private: vector m_endSymbols; PolylineD m_polyline; + routing::turns::TurnsGeomT m_turnsGeom; m2::RectD m_rect; mutable graphics::DisplayList * m_dList = nullptr; }; + +bool clipArrowBodyAndGetArrowDirection(vector & ptsTurn, pair & arrowDirection, + size_t turnIndex, double beforeTurn, double afterTurn, double arrowLength); diff --git a/routing/osrm_router.cpp b/routing/osrm_router.cpp index 9d231390e4..373e34b0e3 100644 --- a/routing/osrm_router.cpp +++ b/routing/osrm_router.cpp @@ -734,6 +734,9 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt turnsDir.push_back(Route::TurnItem(points.size() - 1, turns::ReachedYourDestination)); FixupTurns(points, turnsDir); + turns::TurnsGeomT turnsGeom; + CalculateTurnGeometry(points, turnsDir, turnsGeom); + #ifdef _DEBUG for (auto t : turnsDir) { @@ -759,6 +762,7 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt route.SetGeometry(points.begin(), points.end()); route.SetTurnInstructions(turnsDir); route.SetSectionTimes(times); + route.SetTurnInstructionsGeometry(turnsGeom); LOG(LDEBUG, ("Estimate time:", estimateTime, "s")); @@ -1140,6 +1144,24 @@ void OsrmRouter::GetTurnDirection(PathData const & node1, turn.m_turn = turns::UTurn; } +void OsrmRouter::CalculateTurnGeometry(vector const & points, Route::TurnsT const & turnsDir, turns::TurnsGeomT & turnsGeom) const +{ + size_t const pointsSz = points.size(); + for (Route::TurnItem const & t : turnsDir) + { + ASSERT(t.m_index < pointsSz, ()); + if (t.m_index == 0 || t.m_index == (pointsSz - 1)) + continue; + + uint32_t const beforePivotCount = 10; + /// afterPivotCount is more because there are half body and the arrow after the pivot point + uint32_t const afterPivotCount = beforePivotCount + 10; + uint32_t const fromIndex = (t.m_index <= beforePivotCount) ? 0 : t.m_index - beforePivotCount; + uint32_t const toIndex = min(pointsSz, t.m_index + afterPivotCount); + turnsGeom.emplace_back(beforePivotCount, points.begin() + fromIndex, points.begin() + toIndex); + } +} + void OsrmRouter::FixupTurns(vector const & points, Route::TurnsT & turnsDir) const { uint32_t exitNum = 0; diff --git a/routing/osrm_router.hpp b/routing/osrm_router.hpp index dae6b1f819..a8704a87ab 100644 --- a/routing/osrm_router.hpp +++ b/routing/osrm_router.hpp @@ -77,6 +77,7 @@ private: void GetTurnDirection(PathData const & node1, PathData const & node2, uint32_t mwmId, Route::TurnItem & turn, string const & fName); + void CalculateTurnGeometry(vector const & points, Route::TurnsT const & turnsDir, turns::TurnsGeomT & turnsGeom) const; void FixupTurns(vector const & points, Route::TurnsT & turnsDir) const; m2::PointD GetPointForTurnAngle(OsrmFtSegMapping::FtSeg const &seg, FeatureType const &ft, m2::PointD const &turnPnt, diff --git a/routing/route.cpp b/routing/route.cpp index c65bfe4060..8407d446d5 100644 --- a/routing/route.cpp +++ b/routing/route.cpp @@ -36,6 +36,7 @@ void Route::Swap(Route & rhs) swap(m_currentTime, rhs.m_currentTime); swap(m_turns, rhs.m_turns); swap(m_times, rhs.m_times); + m_turnsGeom.swap(rhs.m_turnsGeom); } void Route::SetTurnInstructions(TurnsT & v) @@ -48,6 +49,11 @@ void Route::SetSectionTimes(TimesT & v) swap(m_times, v); } +void Route::SetTurnInstructionsGeometry(turns::TurnsGeomT & v) +{ + swap(m_turnsGeom, v); +} + double Route::GetDistance() const { ASSERT(!m_segDistance.empty(), ()); diff --git a/routing/route.hpp b/routing/route.hpp index 54d8e5389c..73d29182d1 100644 --- a/routing/route.hpp +++ b/routing/route.hpp @@ -65,6 +65,7 @@ public: void SetTurnInstructions(TurnsT & v); void SetSectionTimes(TimesT & v); + void SetTurnInstructionsGeometry(turns::TurnsGeomT & v); // Time measure are seconds uint32_t GetAllTime() const; @@ -72,6 +73,7 @@ public: string const & GetRouterId() const { return m_router; } m2::PolylineD const & GetPoly() const { return m_poly; } + turns::TurnsGeomT const & GetTurnsGeometry() const { return m_turnsGeom; } string const & GetName() const { return m_name; } bool IsValid() const { return (m_poly.GetSize() > 1); } @@ -132,6 +134,8 @@ private: TurnsT m_turns; TimesT m_times; + turns::TurnsGeomT m_turnsGeom; + /// Cached result iterator for last MoveIterator query. mutable IterT m_current; mutable double m_currentTime; diff --git a/routing/turns.hpp b/routing/turns.hpp index 14501bd3fa..c44dd7c7fa 100644 --- a/routing/turns.hpp +++ b/routing/turns.hpp @@ -1,6 +1,9 @@ #pragma once #include "../std/string.hpp" +#include "../std/vector.hpp" + +#include "../geometry/point2d.hpp" namespace routing { @@ -40,6 +43,19 @@ enum TurnDirection }; +struct TurnGeom +{ + TurnGeom(uint32_t turnIndex, vector::const_iterator b, vector::const_iterator e) + : m_turnIndex(turnIndex), m_points(b, e) + { + } + + uint32_t m_turnIndex; + vector m_points; +}; + +typedef vector TurnsGeomT; + string const & GetTurnString(TurnDirection turn); bool IsLeftTurn(TurnDirection t);