diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 023f771aae..be4e891825 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -203,11 +203,12 @@ void Framework::Get3dMode(bool & allow3d, bool & allow3dBuildings) m_work.Load3dMode(allow3d, allow3dBuildings); } -void Framework::SetChoosePositionMode(bool isChoosePositionMode) +void Framework::SetChoosePositionMode(bool isChoosePositionMode, bool isBusiness, + bool hasPosition, m2::PointD const & position) { m_isChoosePositionMode = isChoosePositionMode; m_work.BlockTapEvents(isChoosePositionMode); - m_work.EnableChoosePositionMode(isChoosePositionMode); + m_work.EnableChoosePositionMode(isChoosePositionMode, isBusiness, hasPosition, position); } Storage & Framework::Storage() @@ -986,11 +987,10 @@ Java_com_mapswithme_maps_Framework_nativeOnBookmarkCategoryChanged(JNIEnv * env, } JNIEXPORT void JNICALL -Java_com_mapswithme_maps_Framework_nativeTurnChoosePositionMode(JNIEnv *, jclass, jboolean turnOn) +Java_com_mapswithme_maps_Framework_nativeTurnChoosePositionMode(JNIEnv *, jclass, jboolean turnOn, jboolean isBusiness) { - ::Framework * fr = frm(); - fr->EnableChoosePositionMode(turnOn); - fr->BlockTapEvents(turnOn); + //TODO(Android team): implement positioning + g_framework->SetChoosePositionMode(turnOn, isBusiness, false /* hasPosition */, m2::PointD()); } JNIEXPORT jboolean JNICALL diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index 7cb9e93bf1..754571d3f9 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -137,7 +137,7 @@ namespace android void Set3dMode(bool allow3d, bool allow3dBuildings); void Get3dMode(bool & allow3d, bool & allow3dBuildings); - void SetChoosePositionMode(bool isChoosePositionMode); + void SetChoosePositionMode(bool isChoosePositionMode, bool isBusiness, bool hasPosition, m2::PointD const & position); void SetupWidget(gui::EWidget widget, float x, float y, dp::Anchor anchor); void ApplyWidgets(); diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java index 8b987c42cf..93b64dedef 100644 --- a/android/src/com/mapswithme/maps/Framework.java +++ b/android/src/com/mapswithme/maps/Framework.java @@ -197,7 +197,7 @@ public class Framework public static native void nativeZoomToPoint(double lat, double lon, int zoom, boolean animate); - public static native void nativeTurnChoosePositionMode(boolean turnedOn); + public static native void nativeTurnChoosePositionMode(boolean turnedOn, boolean isBusiness); public static native boolean nativeIsDownloadedMapAtScreenCenter(); diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 6b1cde5da2..fabffc1f61 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -400,7 +400,7 @@ public class MwmActivity extends BaseMwmFragmentActivity { UiUtils.showIf(show, mPositionChooser); setFullscreen(show); - Framework.nativeTurnChoosePositionMode(show); + Framework.nativeTurnChoosePositionMode(show, false /* isBusiness */); //TODO(Android team): set isBusiness correctly closePlacePage(); mSearchController.hide(); } diff --git a/drape/overlay_tree.cpp b/drape/overlay_tree.cpp index b50317e67e..394ce7e0a6 100644 --- a/drape/overlay_tree.cpp +++ b/drape/overlay_tree.cpp @@ -8,6 +8,7 @@ namespace dp int const kFrameUpdatePeriod = 10; int const kAverageHandlesCount[dp::OverlayRanksCount] = { 300, 200, 50 }; +int const kInvalidFrame = -1; namespace { @@ -59,8 +60,9 @@ private: } // namespace OverlayTree::OverlayTree() - : m_frameCounter(-1) + : m_frameCounter(kInvalidFrame) , m_followingMode(false) + , m_isDisplacementEnabled(true) { for (size_t i = 0; i < m_handles.size(); i++) m_handles[i].reserve(kAverageHandlesCount[i]); @@ -73,14 +75,14 @@ bool OverlayTree::Frame() m_frameCounter++; if (m_frameCounter >= kFrameUpdatePeriod) - m_frameCounter = -1; + m_frameCounter = kInvalidFrame; return IsNeedUpdate(); } bool OverlayTree::IsNeedUpdate() const { - return m_frameCounter == -1; + return m_frameCounter == kInvalidFrame; } void OverlayTree::StartOverlayPlacing(ScreenBase const & screen) @@ -97,11 +99,11 @@ void OverlayTree::StartOverlayPlacing(ScreenBase const & screen) void OverlayTree::Remove(ref_ptr handle) { - if (m_frameCounter == -1) + if (m_frameCounter == kInvalidFrame) return; if (m_handlesCache.find(handle) != m_handlesCache.end()) - m_frameCounter = -1; + m_frameCounter = kInvalidFrame; } void OverlayTree::Add(ref_ptr handle) @@ -161,6 +163,12 @@ void OverlayTree::InsertHandle(ref_ptr handle, ScreenBase const & modelView = GetModelView(); m2::RectD const pixelRect = handle->GetExtendedPixelRect(modelView); + if (!m_isDisplacementEnabled) + { + m_handlesCache.insert(handle); + TBase::Add(handle, pixelRect); + return; + } TOverlayContainer rivals; HandleComparator comparator(true /* enableMask */, m_followingMode); @@ -383,6 +391,12 @@ void OverlayTree::SetFollowingMode(bool mode) m_followingMode = mode; } +void OverlayTree::SetDisplacementEnabled(bool enabled) +{ + m_isDisplacementEnabled = enabled; + m_frameCounter = kInvalidFrame; +} + #ifdef COLLECT_DISPLACEMENT_INFO OverlayTree::TDisplacementInfo const & OverlayTree::GetDisplacementInfo() const diff --git a/drape/overlay_tree.hpp b/drape/overlay_tree.hpp index 817680ff45..3b11413352 100644 --- a/drape/overlay_tree.hpp +++ b/drape/overlay_tree.hpp @@ -63,6 +63,8 @@ public: void SetFollowingMode(bool mode); + void SetDisplacementEnabled(bool enabled); + #ifdef COLLECT_DISPLACEMENT_INFO struct DisplacementData { @@ -90,6 +92,8 @@ private: unordered_set, detail::OverlayHasher> m_handlesCache; bool m_followingMode; + bool m_isDisplacementEnabled; + #ifdef COLLECT_DISPLACEMENT_INFO TDisplacementInfo m_displacementInfo; #endif diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index a0afd187c7..992c35ec54 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -70,7 +70,7 @@ DrapeEngine::DrapeEngine(Params && params) RecacheGui(false); if (params.m_showChoosePositionMark) - EnableChoosePositionMode(true); + EnableChoosePositionMode(true, move(params.m_boundAreaTriangles), false, m2::PointD()); ResizeImpl(m_viewport.GetWidth(), m_viewport.GetHeight()); } @@ -436,26 +436,27 @@ void DrapeEngine::ClearGpsTrackPoints() MessagePriority::Normal); } -void DrapeEngine::EnableChoosePositionMode(bool enable) +void DrapeEngine::EnableChoosePositionMode(bool enable, vector && boundAreaTriangles, + bool hasPosition, m2::PointD const & position) { m_choosePositionMode = enable; + bool kineticScroll = m_kineticScrollEnabled; if (enable) { StopLocationFollow(); m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp(), MessagePriority::High); - m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(false /* enabled */), - MessagePriority::High); + kineticScroll = false; } else { RecacheGui(true); - m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(m_kineticScrollEnabled), - MessagePriority::High); } + m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(enable, move(boundAreaTriangles), + kineticScroll, hasPosition, position), + MessagePriority::High); } void DrapeEngine::BlockTapEvents(bool block) diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index 19da53eb08..dbfecf1933 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -13,6 +13,7 @@ #include "geometry/polyline2d.hpp" #include "geometry/screenbase.hpp" +#include "geometry/triangle2d.hpp" #include "base/strings_bundle.hpp" @@ -43,6 +44,7 @@ public: bool allow3dBuildings, bool blockTapEvents, bool showChoosePositionMark, + vector && boundAreaTriangles, bool firstLaunch) : m_factory(factory) , m_stringsBundle(stringBundle) @@ -54,6 +56,7 @@ public: , m_allow3dBuildings(allow3dBuildings) , m_blockTapEvents(blockTapEvents) , m_showChoosePositionMark(showChoosePositionMark) + , m_boundAreaTriangles(move(boundAreaTriangles)) , m_isFirstLaunch(firstLaunch) {} @@ -67,6 +70,7 @@ public: bool m_allow3dBuildings; bool m_blockTapEvents; bool m_showChoosePositionMark; + vector m_boundAreaTriangles; bool m_isFirstLaunch; }; @@ -129,7 +133,8 @@ public: void UpdateGpsTrackPoints(vector && toAdd, vector && toRemove); void ClearGpsTrackPoints(); - void EnableChoosePositionMode(bool enable); + void EnableChoosePositionMode(bool enable, vector && boundAreaTriangles, + bool hasPosition, m2::PointD const & position); void BlockTapEvents(bool block); void SetKineticScrollEnabled(bool enabled); diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 5e2cacad1c..26a8863c43 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -119,6 +119,7 @@ FrontendRenderer::FrontendRenderer(Params const & params) , m_enable3dBuildings(params.m_allow3dBuildings) , m_isIsometry(false) , m_blockTapEvents(params.m_blockTapEvents) + , m_choosePositionMode(false) , m_viewport(params.m_viewport) , m_modelViewChangedFn(params.m_modelViewChangedFn) , m_tapEventInfoFn(params.m_tapEventFn) @@ -347,6 +348,16 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) m_guiRenderer = move(renderer); else m_guiRenderer->Merge(make_ref(renderer)); + + bool oldMode = m_choosePositionMode; + m_choosePositionMode = m_guiRenderer->HasWidget(gui::WIDGET_CHOOSE_POSITION_MARK); + if (oldMode != m_choosePositionMode) + { + ScreenBase const & screen = m_userEventStream.GetCurrentScreen(); + CheckIsometryMinScale(screen); + UpdateDisplacementEnabled(); + InvalidateRect(screen.ClipRect()); + } break; } @@ -694,6 +705,27 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::SetAddNewPlaceMode: + { + ref_ptr msg = message; + m_userEventStream.SetKineticScrollEnabled(msg->IsKineticScrollEnabled()); + m_dragBoundArea = msg->AcceptBoundArea(); + if (msg->IsEnabled()) + { + if (!m_dragBoundArea.empty()) + { + PullToBoundArea(true /* randomPlace */, true /* applyZoom */); + } + else + { + m2::PointD const pt = msg->HasPosition()? msg->GetPosition() : + m_userEventStream.GetCurrentScreen().GlobalRect().Center(); + AddUserEvent(SetCenterEvent(pt, scales::GetAddNewPlaceScale(), true)); + } + } + break; + } + case Message::Invalidate: { // Do nothing here, new frame will be rendered because of this message processing. @@ -882,6 +914,22 @@ void FrontendRenderer::PrepareGpsTrackPoints(size_t pointsCount) MessagePriority::Normal); } +void FrontendRenderer::PullToBoundArea(bool randomPlace, bool applyZoom) +{ + if (m_dragBoundArea.empty()) + return; + + ScreenBase const & screen = m_userEventStream.GetCurrentScreen(); + m2::PointD const center = screen.GlobalRect().Center(); + if (!m2::IsPointInsideTriangles(center, m_dragBoundArea)) + { + m2::PointD const dest = randomPlace ? m2::GetRandomPointInsideTriangles(m_dragBoundArea) : + m2::ProjectPointToTriangles(center, m_dragBoundArea); + int const zoom = applyZoom ? scales::GetAddNewPlaceScale() : m_currentZoomLevel; + AddUserEvent(SetCenterEvent(dest, zoom, true)); + } +} + void FrontendRenderer::BeginUpdateOverlayTree(ScreenBase const & modelView) { if (m_overlayTree->Frame()) @@ -1189,10 +1237,10 @@ void FrontendRenderer::DisablePerspective() AddUserEvent(DisablePerspectiveEvent()); } -void FrontendRenderer::CheckIsometryMinScale(const ScreenBase &screen) +void FrontendRenderer::CheckIsometryMinScale(ScreenBase const & screen) { bool const isScaleAllowableIn3d = UserEventStream::IsScaleAllowableIn3d(m_currentZoomLevel); - bool const isIsometry = m_enable3dBuildings && isScaleAllowableIn3d; + bool const isIsometry = m_enable3dBuildings && !m_choosePositionMode && isScaleAllowableIn3d; if (m_isIsometry != isIsometry) { m_isIsometry = isIsometry; @@ -1224,6 +1272,15 @@ void FrontendRenderer::ResolveZoomLevel(ScreenBase const & screen) CheckIsometryMinScale(screen); CheckPerspectiveMinScale(); + UpdateDisplacementEnabled(); +} + +void FrontendRenderer::UpdateDisplacementEnabled() +{ + if (m_choosePositionMode) + m_overlayTree->SetDisplacementEnabled(m_currentZoomLevel < scales::GetAddNewPlaceScale()); + else + m_overlayTree->SetDisplacementEnabled(true); } void FrontendRenderer::OnTap(m2::PointD const & pt, bool isLongTap) @@ -1298,6 +1355,7 @@ void FrontendRenderer::OnDragStarted() void FrontendRenderer::OnDragEnded(m2::PointD const & distance) { m_myPositionController->DragEnded(distance); + PullToBoundArea(false /* randomPlace */, false /* applyZoom */); } void FrontendRenderer::OnScaleStarted() @@ -1328,6 +1386,7 @@ void FrontendRenderer::CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) con void FrontendRenderer::OnScaleEnded() { m_myPositionController->ScaleEnded(); + PullToBoundArea(false /* randomPlace */, false /* applyZoom */); } void FrontendRenderer::OnAnimationStarted(ref_ptr anim) diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index 461dc51aba..4e9e4d363d 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -32,6 +32,7 @@ #include "platform/location.hpp" #include "geometry/screenbase.hpp" +#include "geometry/triangle2d.hpp" #include "std/function.hpp" #include "std/map.hpp" @@ -168,6 +169,7 @@ private: TTilesCollection ResolveTileKeys(ScreenBase const & screen); void ResolveZoomLevel(ScreenBase const & screen); + void UpdateDisplacementEnabled(); void CheckPerspectiveMinScale(); void CheckIsometryMinScale(ScreenBase const & screen); @@ -229,6 +231,8 @@ private: void PrepareGpsTrackPoints(size_t pointsCount); + void PullToBoundArea(bool randomPlace, bool applyZoom); + private: drape_ptr m_gpuProgramManager; @@ -272,6 +276,8 @@ private: bool m_blockTapEvents; + bool m_choosePositionMode; + Viewport m_viewport; UserEventStream m_userEventStream; TModelViewChanged m_modelViewChangedFn; @@ -309,6 +315,8 @@ private: unique_ptr m_pendingFollowRoute; + vector m_dragBoundArea; + #ifdef DEBUG bool m_isTeardowned; #endif diff --git a/drape_frontend/gui/layer_render.cpp b/drape_frontend/gui/layer_render.cpp index 8ec711ab78..3282fc89ca 100644 --- a/drape_frontend/gui/layer_render.cpp +++ b/drape_frontend/gui/layer_render.cpp @@ -129,6 +129,11 @@ void LayerRenderer::OnTouchCancel(m2::RectD const & touchArea) } } +bool LayerRenderer::HasWidget(EWidget widget) const +{ + return m_renderers.find(widget) != m_renderers.end(); +} + namespace { diff --git a/drape_frontend/gui/layer_render.hpp b/drape_frontend/gui/layer_render.hpp index dd8f63e444..f48cf76c0c 100644 --- a/drape_frontend/gui/layer_render.hpp +++ b/drape_frontend/gui/layer_render.hpp @@ -35,6 +35,8 @@ public: void OnTouchUp(m2::RectD const & touchArea); void OnTouchCancel(m2::RectD const & touchArea); + bool HasWidget(EWidget widget) const; + private: void DestroyRenderers(); diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp index 52bfbde85e..ac33cf2521 100644 --- a/drape_frontend/message.hpp +++ b/drape_frontend/message.hpp @@ -54,7 +54,8 @@ public: ShowChoosePositionMark, SetKineticScrollEnabled, BlockTapEvents, - SetTimeInBackground + SetTimeInBackground, + SetAddNewPlaceMode }; virtual ~Message() {} diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp index 84556e7cdd..b42da8c053 100644 --- a/drape_frontend/message_subclasses.hpp +++ b/drape_frontend/message_subclasses.hpp @@ -18,6 +18,7 @@ #include "geometry/polyline2d.hpp" #include "geometry/rect2d.hpp" #include "geometry/screenbase.hpp" +#include "geometry/triangle2d.hpp" #include "drape/glstate.hpp" #include "drape/pointers.hpp" @@ -328,6 +329,33 @@ private: bool m_enabled; }; +class SetAddNewPlaceModeMessage : public Message +{ +public: + SetAddNewPlaceModeMessage(bool enable, vector && boundArea, bool enableKineticScroll, + bool hasPosition, m2::PointD const & position) + : m_enable(enable) + , m_boundArea(move(boundArea)) + , m_enableKineticScroll(enableKineticScroll) + , m_hasPosition(hasPosition) + , m_position(position) + {} + + Type GetType() const override { return Message::SetAddNewPlaceMode; } + vector && AcceptBoundArea() { return move(m_boundArea); } + bool IsEnabled() const { return m_enable; } + bool IsKineticScrollEnabled() const { return m_enableKineticScroll; } + bool HasPosition() const { return m_hasPosition; } + m2::PointD const & GetPosition() const { return m_position; } + +private: + bool m_enable; + vector m_boundArea; + bool m_enableKineticScroll; + bool m_hasPosition; + m2::PointD m_position; +}; + class BlockTapEventsMessage : public Message { public: diff --git a/drape_frontend/my_position_controller.hpp b/drape_frontend/my_position_controller.hpp index 65200a910c..30ba569d01 100644 --- a/drape_frontend/my_position_controller.hpp +++ b/drape_frontend/my_position_controller.hpp @@ -95,12 +95,14 @@ public: bool IsRouteFollowingActive() const; bool IsWaitingForTimers() const; + bool IsWaitingForLocation() const; + m2::PointD GetDrawablePosition() const; + private: bool IsModeChangeViewport() const; void ChangeMode(location::EMyPositionMode newMode); void SetDirection(double bearing); - bool IsWaitingForLocation() const; bool IsVisible() const { return m_isVisible; } void SetIsVisible(bool isVisible) { m_isVisible = isVisible; } @@ -114,7 +116,6 @@ private: m2::PointD GetRotationPixelCenter() const; m2::PointD GetRoutingRotationPixelCenter() const; - m2::PointD GetDrawablePosition() const; double GetDrawableAzimut() const; void CheckAnimFinished() const; void CreateAnim(m2::PointD const & oldPos, double oldAzimut, ScreenBase const & screen); diff --git a/geometry/triangle2d.cpp b/geometry/triangle2d.cpp index 1bd0f56f4c..c143729d72 100644 --- a/geometry/triangle2d.cpp +++ b/geometry/triangle2d.cpp @@ -1,8 +1,14 @@ #include "triangle2d.hpp" +#include "distance.hpp" #include "robust_orientation.hpp" #include "segment2d.hpp" +#include "base/math.hpp" + +#include "std/chrono.hpp" +#include "std/random.hpp" + using namespace m2::robust; namespace m2 @@ -37,4 +43,63 @@ bool IsPointStrictlyInsideTriangle(m2::PointD const & pt, m2::PointD const & p1, (s1 < 0.0 && s2 < 0.0 && s3 < 0.0)); } +bool IsPointInsideTriangles(m2::PointD const & pt, vector const & v) +{ + for (auto const & triangle : v) + { + if (IsPointInsideTriangle(pt, triangle.p1(), triangle.p2(), triangle.p3())) + return true; + } + return false; +} + +m2::PointD GetRandomPointInsideTriangle(m2::TriangleD const & t) +{ + size_t kDistribMax = 1000; + + default_random_engine engine(system_clock::now().time_since_epoch().count()); + uniform_int_distribution<> distrib(0, kDistribMax); + double const r1 = sqrt(static_cast(distrib(engine)) / kDistribMax); + double const r2 = static_cast(distrib(engine)) / kDistribMax; + return t.m_points[0] * (1.0 - r1) + t.m_points[1] * r1 * (1.0 - r2) + t.m_points[2] * r2 * r1; +} + +m2::PointD GetRandomPointInsideTriangles(vector const & v) +{ + if (v.empty()) + return m2::PointD(); + + default_random_engine engine(system_clock::now().time_since_epoch().count()); + uniform_int_distribution<> distrib(0, v.size() - 1); + return GetRandomPointInsideTriangle(v[distrib(engine)]); +} + +m2::PointD ProjectPointToTriangles(m2::PointD const & pt, vector const & v) +{ + if (v.empty()) + return pt; + + auto distToLine = m2::DistanceToLineSquare(); + int minT = -1; + int minI = -1; + double minDist = numeric_limits::max(); + for (int t = 0; t < static_cast(v.size()); t++) + { + for (int i = 0; i < 3; i++) + { + distToLine.SetBounds(v[t].m_points[i], v[t].m_points[(i + 1) % 3]); + double const dist = distToLine(pt); + if (dist < minDist) + { + minDist = dist; + minT = t; + minI = i; + } + } + } + auto projectToLine = m2::ProjectionToSection(); + projectToLine.SetBounds(v[minT].m_points[minI], v[minT].m_points[(minI + 1) % 3]); + return projectToLine(pt); +} + } // namespace m2; diff --git a/geometry/triangle2d.hpp b/geometry/triangle2d.hpp index 6dbfebde85..e63e8ee832 100644 --- a/geometry/triangle2d.hpp +++ b/geometry/triangle2d.hpp @@ -2,15 +2,43 @@ #include "point2d.hpp" +#include "std/vector.hpp" + namespace m2 { +template struct Triangle +{ + Point m_points[3]; + + Triangle(Point const & p1, Point const & p2, Point const & p3) + { + m_points[0] = p1; + m_points[1] = p2; + m_points[2] = p3; + } + + Point const & p1() const { return m_points[0]; } + Point const & p2() const { return m_points[1]; } + Point const & p3() const { return m_points[2]; } +}; + +using TriangleF = Triangle; +using TriangleD = Triangle; + template double GetTriangleArea(Point const & p1, Point const & p2, Point const & p3) { return 0.5 * fabs((p2.x - p1.x)*(p3.y - p1.y) - (p3.x - p1.x)*(p2.y - p1.y)); } +m2::PointD GetRandomPointInsideTriangle(m2::TriangleD const & t); +m2::PointD GetRandomPointInsideTriangles(vector const & v); + +// Project point to the nearest edge of the nearest triangle from list of triangles. +// pt must be outside triangles. +m2::PointD ProjectPointToTriangles(m2::PointD const & pt, vector const & v); + /// @param[in] pt - Point to check /// @param[in] p1, p2, p3 - Triangle //@{ @@ -19,6 +47,8 @@ bool IsPointInsideTriangle(m2::PointD const & pt, m2::PointD const & p1, bool IsPointStrictlyInsideTriangle(m2::PointD const & pt, m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3); + +bool IsPointInsideTriangles(m2::PointD const & pt, vector const & v); //@} } // namespace m2 diff --git a/indexer/scales.hpp b/indexer/scales.hpp index 2e0b21048a..10d9cc1c4a 100644 --- a/indexer/scales.hpp +++ b/indexer/scales.hpp @@ -18,14 +18,16 @@ namespace scales constexpr int GetUpperCountryScale() { return GetUpperWorldScale() + 1; } /// Upper scale for user comfort view (e.g. location zoom). constexpr int GetUpperComfortScale() { return UPPER_STYLE_SCALE - 2; } - /// Default navigation mode scale + /// Default navigation mode scale. constexpr int GetNavigationScale() { return UPPER_STYLE_SCALE - 3; } - /// Default pedestrian navigation mode scale + /// Default pedestrian navigation mode scale. constexpr int GetPedestrianNavigationScale() { return UPPER_STYLE_SCALE - 2; } - /// Default navigation 3d mode scale + /// Default navigation 3d mode scale. constexpr int GetNavigation3dScale() { return UPPER_STYLE_SCALE - 2; } - /// Default pedestrian navigation 3d mode scale + /// Default pedestrian navigation 3d mode scale. constexpr int GetPedestrianNavigation3dScale() { return UPPER_STYLE_SCALE - 2; } + /// Default scale in adding-new-place mode. + constexpr int GetAddNewPlaceScale() { return 18; } int GetMinAllowableIn3dScale(); diff --git a/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h b/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h index afbe13ece1..b3fc3166dc 100644 --- a/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h +++ b/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h @@ -1,5 +1,9 @@ +#include "geometry/point2d.hpp" + @interface MWMAddPlaceNavigationBar : SolidTouchView -+ (void)showInSuperview:(nonnull UIView *)superview doneBlock:(nonnull TMWMVoidBlock)done cancelBlock:(nonnull TMWMVoidBlock)cancel; ++ (void)showInSuperview:(UIView *)superview isBusiness:(BOOL)isBusiness + applyPosition:(BOOL)applyPosition position:(m2::PointD const &)position + doneBlock:(TMWMVoidBlock)done cancelBlock:(TMWMVoidBlock)cancel; @end diff --git a/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm b/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm index 49338c74af..3fd88dae6e 100644 --- a/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm +++ b/iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm @@ -12,7 +12,9 @@ @implementation MWMAddPlaceNavigationBar -+ (void)showInSuperview:(UIView *)superview doneBlock:(TMWMVoidBlock)done cancelBlock:(TMWMVoidBlock)cancel ++ (void)showInSuperview:(UIView *)superview isBusiness:(BOOL)isBusiness + applyPosition:(BOOL)applyPosition position:(m2::PointD const &)position + doneBlock:(TMWMVoidBlock)done cancelBlock:(TMWMVoidBlock)cancel { MWMAddPlaceNavigationBar * navBar = [[[NSBundle mainBundle] loadNibNamed:self.className owner:nil options:nil] firstObject]; navBar.width = superview.width; @@ -22,13 +24,13 @@ [navBar setNeedsLayout]; navBar.origin = {0., -navBar.height}; [superview addSubview:navBar]; - [navBar show]; + [navBar show:isBusiness applyPosition:applyPosition position:position]; } -- (void)show +- (void)show:(BOOL)enableBounds applyPosition:(BOOL)applyPosition position:(m2::PointD const &)position { auto & f = GetFramework(); - f.EnableChoosePositionMode(true); + f.EnableChoosePositionMode(true /* enable */, enableBounds, applyPosition, position); f.BlockTapEvents(true); [UIView animateWithDuration:kDefaultAnimationDuration animations:^ @@ -40,7 +42,7 @@ - (void)dismiss { auto & f = GetFramework(); - f.EnableChoosePositionMode(false); + f.EnableChoosePositionMode(false /* enable */, false /* enableBounds */, false /* applyPosition */, m2::PointD()); f.BlockTapEvents(false); [UIView animateWithDuration:kDefaultAnimationDuration animations:^ diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.h b/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.h index af66ba8c48..b7cda5a317 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.h +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.h @@ -9,7 +9,7 @@ - (void)actionDownloadMaps:(mwm::DownloaderMode)mode; - (void)closeInfoScreens; -- (void)addPlace; +- (void)addPlace:(BOOL)isBusiness hasPoint:(BOOL)hasPoint point:(m2::PointD const &)point; - (void)didFinishAddingPlace; @end diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.mm index 22081c793f..2ad3c43d8c 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/BottomMenu/MWMBottomMenuViewController.mm @@ -351,7 +351,7 @@ typedef NS_ENUM(NSUInteger, MWMBottomMenuViewCell) { [Statistics logEvent:kStatEditorAddClick withParameters:@{kStatValue : kStatMenu}]; self.state = self.restoreState; - [self.delegate addPlace]; + [self.delegate addPlace:NO hasPoint:NO point:m2::PointD()]; } - (void)menuActionDownloadMaps diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm index dc9298bce9..70a41a3a94 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm @@ -305,14 +305,15 @@ extern NSString * const kAlohalyticsTapEventKey; #pragma mark - MWMBottomMenuControllerProtocol && MWMPlacePageViewManagerProtocol -- (void)addPlace +- (void)addPlace:(BOOL)isBusiness hasPoint:(BOOL)hasPoint point:(m2::PointD const &)point { self.menuState = MWMBottomMenuStateHidden; static_cast(self.ownerController.view).widgetsManager.fullScreen = YES; [self.placePageManager dismissPlacePage]; self.searchManager.state = MWMSearchManagerStateHidden; - [MWMAddPlaceNavigationBar showInSuperview:self.ownerController.view doneBlock:^ + [MWMAddPlaceNavigationBar showInSuperview:self.ownerController.view + isBusiness:isBusiness applyPosition:hasPoint position:point doneBlock:^ { auto & f = GetFramework(); @@ -361,12 +362,6 @@ extern NSString * const kAlohalyticsTapEventKey; } } -- (void)addBusinessToPoint:(m2::PointD const &)point -{ - GetFramework().SetViewportCenter(point); - [self addPlace]; -} - - (void)updateStatusBarStyle { [self.ownerController updateStatusBarStyle]; diff --git a/iphone/Maps/Classes/MWMPlacePageViewManager.mm b/iphone/Maps/Classes/MWMPlacePageViewManager.mm index 325e244df2..0e9c81ff09 100644 --- a/iphone/Maps/Classes/MWMPlacePageViewManager.mm +++ b/iphone/Maps/Classes/MWMPlacePageViewManager.mm @@ -260,12 +260,12 @@ extern NSString * const kBookmarksChangedNotification; - (void)addBusiness { - [self.delegate addBusinessToPoint:self.entity.mercator]; + [self.delegate addPlace:YES hasPoint:NO point:m2::PointD()]; } - (void)addPlace { - [self.delegate addPlace]; + [self.delegate addPlace:NO hasPoint:YES point:self.entity.mercator]; } - (void)addBookmark diff --git a/iphone/Maps/Classes/MWMPlacePageViewManagerDelegate.h b/iphone/Maps/Classes/MWMPlacePageViewManagerDelegate.h index 104d40707b..6421213899 100644 --- a/iphone/Maps/Classes/MWMPlacePageViewManagerDelegate.h +++ b/iphone/Maps/Classes/MWMPlacePageViewManagerDelegate.h @@ -9,7 +9,6 @@ - (void)updateStatusBarStyle; - (void)apiBack; - (void)placePageDidClose; -- (void)addBusinessToPoint:(m2::PointD const &)point; -- (void)addPlace; +- (void)addPlace:(BOOL)isBusiness hasPoint:(BOOL)hasPoint point:(m2::PointD const &)point; @end \ No newline at end of file diff --git a/map/framework.cpp b/map/framework.cpp index b69c1b8e7a..5d4f9922eb 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -60,6 +60,7 @@ #include "geometry/angles.hpp" #include "geometry/distance_on_sphere.hpp" +#include "geometry/triangle2d.hpp" #include "base/math.hpp" #include "base/timer.hpp" @@ -1489,7 +1490,7 @@ void Framework::CreateDrapeEngine(ref_ptr contextFactory, params.m_visualScale, move(params.m_widgetsInitInfo), make_pair(params.m_initialMyPositionState, params.m_hasMyPositionState), allow3dBuildings, params.m_isChoosePositionMode, - params.m_isChoosePositionMode, params.m_isFirstLaunch); + params.m_isChoosePositionMode, GetSelectedFeatureTriangles(), params.m_isFirstLaunch); m_drapeEngine = make_unique_dp(move(p)); AddViewportListener([this](ScreenBase const & screen) @@ -1858,6 +1859,7 @@ void Framework::ActivateMapSelection(bool needAnimation, df::SelectionShape::ESe place_page::Info const & info) const { ASSERT_NOT_EQUAL(selectionType, df::SelectionShape::OBJECT_EMPTY, ("Empty selections are impossible.")); + m_selectedFeature = info.GetID(); CallDrapeFunction(bind(&df::DrapeEngine::SelectObject, _1, selectionType, info.GetMercator(), needAnimation)); if (m_activateMapSelectionFn) @@ -2418,9 +2420,32 @@ void Framework::Load3dMode(bool & allow3d, bool & allow3dBuildings) allow3dBuildings = true; } -void Framework::EnableChoosePositionMode(bool enable) +void Framework::EnableChoosePositionMode(bool enable, bool enableBounds, bool applyPosition, m2::PointD const & position) { - CallDrapeFunction(bind(&df::DrapeEngine::EnableChoosePositionMode, _1, enable)); + if (m_drapeEngine != nullptr) + m_drapeEngine->EnableChoosePositionMode(enable, enableBounds ? GetSelectedFeatureTriangles() : vector(), + applyPosition, position); +} + +vector Framework::GetSelectedFeatureTriangles() const +{ + vector triangles; + if (m_selectedFeature.IsValid()) + { + Index::FeaturesLoaderGuard const guard(m_model.GetIndex(), m_selectedFeature.m_mwmId); + FeatureType ft; + guard.GetFeatureByIndex(m_selectedFeature.m_index, ft); + if (ftypes::IsBuildingChecker::Instance()(feature::TypesHolder(ft))) + { + triangles.reserve(10); + ft.ForEachTriangle([&](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + triangles.push_back(m2::TriangleD(p1, p2, p3)); + }, scales::GetUpperScale()); + } + m_selectedFeature = FeatureID(); + } + return triangles; } void Framework::BlockTapEvents(bool block) diff --git a/map/framework.hpp b/map/framework.hpp index 18ca2d8a50..8666b610c1 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -276,7 +276,7 @@ public: void InvalidateRendering(); - void EnableChoosePositionMode(bool enable); + void EnableChoosePositionMode(bool enable, bool enableBounds, bool applyPosition, m2::PointD const & position); void BlockTapEvents(bool block); using TCurrentCountryChanged = function; @@ -303,6 +303,12 @@ private: TActivateMapSelectionFn m_activateMapSelectionFn; TDeactivateMapSelectionFn m_deactivateMapSelectionFn; + /// Here we store last selected feature to get its polygons in case of adding organization. + mutable FeatureID m_selectedFeature; + +private: + vector GetSelectedFeatureTriangles() const; + public: /// @name GPS location updates routine. diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index 3988b3df26..916abe3185 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -231,12 +231,12 @@ void DrawWidget::SliderReleased() void DrawWidget::ChoosePositionModeEnable() { m_framework->BlockTapEvents(true); - m_framework->EnableChoosePositionMode(true); + m_framework->EnableChoosePositionMode(true, false, false, m2::PointD()); } void DrawWidget::ChoosePositionModeDisable() { - m_framework->EnableChoosePositionMode(false); + m_framework->EnableChoosePositionMode(false, false, false, m2::PointD()); m_framework->BlockTapEvents(false); }