Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaria Volvenkova <d.volvenkova@corp.mail.ru>2017-08-18 14:32:01 +0300
committerRoman Kuznetsov <r.kuznetsow@gmail.com>2017-08-22 17:51:04 +0300
commit96b79413b50e81ba7b0053e590e7502acbc3c5b3 (patch)
tree0820cee64843d10285362b0aa4deba3642543a1d
parent31233e6b673d8a8eac39b17e173c40dd1ee6f7e8 (diff)
Rounding of spline corners for path text.beta-973
-rw-r--r--drape_frontend/drape_frontend_tests/CMakeLists.txt1
-rw-r--r--drape_frontend/drape_frontend_tests/drape_frontend_tests.pro1
-rw-r--r--drape_frontend/drape_frontend_tests/path_text_test.cpp57
-rw-r--r--drape_frontend/path_text_handle.cpp115
-rw-r--r--drape_frontend/path_text_handle.hpp3
-rw-r--r--drape_frontend/text_layout.cpp30
6 files changed, 182 insertions, 25 deletions
diff --git a/drape_frontend/drape_frontend_tests/CMakeLists.txt b/drape_frontend/drape_frontend_tests/CMakeLists.txt
index 4484e5ad15..9d6cb89d90 100644
--- a/drape_frontend/drape_frontend_tests/CMakeLists.txt
+++ b/drape_frontend/drape_frontend_tests/CMakeLists.txt
@@ -25,6 +25,7 @@ set(
SRC
compile_shaders_test.cpp
navigator_test.cpp
+ path_text_test.cpp
shader_def_for_tests.cpp
shader_def_for_tests.hpp
user_event_stream_tests.cpp
diff --git a/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro b/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro
index 660eeee57d..49d1473aed 100644
--- a/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro
+++ b/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro
@@ -35,6 +35,7 @@ SOURCES += \
../../testing/testingmain.cpp \
compile_shaders_test.cpp \
navigator_test.cpp \
+ path_text_test.cpp \
shader_def_for_tests.cpp \
user_event_stream_tests.cpp \
diff --git a/drape_frontend/drape_frontend_tests/path_text_test.cpp b/drape_frontend/drape_frontend_tests/path_text_test.cpp
new file mode 100644
index 0000000000..00f2487096
--- /dev/null
+++ b/drape_frontend/drape_frontend_tests/path_text_test.cpp
@@ -0,0 +1,57 @@
+#include "testing/testing.hpp"
+
+#include "drape_frontend/path_text_handle.hpp"
+
+#include "base/logging.hpp"
+
+namespace
+{
+bool IsSmooth(m2::Spline const & spline)
+{
+ for (size_t i = 0, sz = spline.GetDirections().size(); i + 1 < sz; ++i)
+ {
+ if (!df::IsValidSplineTurn(spline.GetDirections()[i], spline.GetDirections()[i + 1]))
+ return false;
+ }
+ return true;
+}
+}
+
+UNIT_TEST(Rounding_Spline)
+{
+ m2::Spline spline1;
+ df::AddPointAndRound(spline1, m2::PointD(0, 200));
+ df::AddPointAndRound(spline1, m2::PointD(0, 0));
+ df::AddPointAndRound(spline1, m2::PointD(200, 0));
+ TEST(IsSmooth(spline1), ());
+ TEST(spline1.GetSize() == 8, ());
+
+ m2::Spline spline2;
+ df::AddPointAndRound(spline2, m2::PointD(-200, 0));
+ df::AddPointAndRound(spline2, m2::PointD(0, 0));
+ df::AddPointAndRound(spline2, m2::PointD(200, 200));
+ df::AddPointAndRound(spline2, m2::PointD(400, 200));
+ TEST(IsSmooth(spline2), ());
+ TEST(spline2.GetSize() == 8, ());
+
+ m2::Spline spline3;
+ df::AddPointAndRound(spline3, m2::PointD(200, 100));
+ df::AddPointAndRound(spline3, m2::PointD(0, 0));
+ df::AddPointAndRound(spline3, m2::PointD(200, 0));
+ TEST(!IsSmooth(spline3), ());
+ TEST(spline3.GetSize() == 3, ());
+
+ m2::Spline spline4;
+ df::AddPointAndRound(spline4, m2::PointD(-200, 5));
+ df::AddPointAndRound(spline4, m2::PointD(0, 0));
+ df::AddPointAndRound(spline4, m2::PointD(200, 5));
+ TEST(IsSmooth(spline4), ());
+ TEST(spline4.GetSize() == 3, ());
+
+ m2::Spline spline5;
+ df::AddPointAndRound(spline5, m2::PointD(200, 5));
+ df::AddPointAndRound(spline5, m2::PointD(0, 0));
+ df::AddPointAndRound(spline5, m2::PointD(200, -5));
+ TEST(!IsSmooth(spline5), ());
+ TEST(spline5.GetSize() == 3, ());
+}
diff --git a/drape_frontend/path_text_handle.cpp b/drape_frontend/path_text_handle.cpp
index 38f83212d5..595e0b3164 100644
--- a/drape_frontend/path_text_handle.cpp
+++ b/drape_frontend/path_text_handle.cpp
@@ -1,8 +1,107 @@
#include "drape_frontend/path_text_handle.hpp"
+#include "drape_frontend/visual_params.hpp"
+
+#include "base/math.hpp"
namespace df
{
+namespace
+{
+double const kValidSplineTurn = 15 * math::pi / 180;
+double const kCosTurn = cos(kValidSplineTurn);
+double const kSinTurn = sin(kValidSplineTurn);
+double const kRoundStep = 23;
+int const kMaxStepsCount = 7;
+
+bool RoundCorner(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3,
+ int leftStepsCount, std::vector<m2::PointD> & roundedCorner)
+{
+ roundedCorner.clear();
+
+ double p1p2Length = (p2 - p1).Length();
+ double const p2p3Length = (p3 - p2).Length();
+
+ auto const dir1 = (p2 - p1) / p1p2Length;
+ auto const dir2 = (p3 - p2) / p2p3Length;
+
+ if (IsValidSplineTurn(dir1, dir2))
+ return false;
+
+ double const vs = df::VisualParams::Instance().GetVisualScale();
+ double const kMinCornerDist = 1.0;
+ if ((p3 - p1).SquaredLength() < kMinCornerDist * vs)
+ {
+ roundedCorner.push_back(p1);
+ return false;
+ }
+ m2::PointD const np1 = p2 - dir1 * std::min(kRoundStep * vs,
+ p1p2Length - p1p2Length / max(leftStepsCount - 1, 2));
+ p1p2Length = (p2 - np1).Length();
+ double const cosCorner = m2::DotProduct(-dir1, dir2);
+ double const sinCorner = fabs(m2::CrossProduct(-dir1, dir2));
+
+ double const p2p3IntersectionLength = (p1p2Length * kSinTurn) / (kSinTurn * cosCorner + kCosTurn * sinCorner);
+
+ if (p2p3IntersectionLength >= p2p3Length)
+ {
+ roundedCorner.push_back(np1);
+ return false;
+ }
+ else
+ {
+ m2::PointD const p = p2 + dir2 * p2p3IntersectionLength;
+ roundedCorner.push_back(np1);
+ roundedCorner.push_back(p);
+ return !IsValidSplineTurn((p - np1).Normalize(), dir2);
+ }
+}
+
+void ReplaceLastCorner(std::vector<m2::PointD> const & roundedCorner, m2::Spline & spline)
+{
+ if (roundedCorner.empty())
+ return;
+ spline.ReplacePoint(roundedCorner.front());
+ for (size_t i = 1, sz = roundedCorner.size(); i < sz; ++i)
+ spline.AddPoint(roundedCorner[i]);
+}
+} // namespace
+
+bool IsValidSplineTurn(m2::PointD const & normalizedDir1,
+ m2::PointD const & normalizedDir2)
+{
+ double const dotProduct = m2::DotProduct(normalizedDir1, normalizedDir2);
+ double const kEps = 1e-5;
+ return dotProduct > kCosTurn || fabs(dotProduct - kCosTurn) < kEps;
+}
+
+void AddPointAndRound(m2::Spline & spline, m2::PointD const & pt)
+{
+ if (spline.GetSize() < 2)
+ {
+ spline.AddPoint(pt);
+ return;
+ }
+
+ auto const dir1 = spline.GetDirections().back();
+ auto const dir2 = (pt - spline.GetPath().back()).Normalize();
+
+ double const dotProduct = m2::DotProduct(dir1, dir2);
+ if (dotProduct < kCosTurn)
+ {
+ int leftStepsCount = static_cast<int>(acos(dotProduct) / kValidSplineTurn);
+ std::vector<m2::PointD> roundedCorner;
+ while (leftStepsCount > 0 && leftStepsCount <= kMaxStepsCount &&
+ RoundCorner(spline.GetPath()[spline.GetSize() - 2],
+ spline.GetPath().back(), pt, leftStepsCount--, roundedCorner))
+ {
+ ReplaceLastCorner(roundedCorner, spline);
+ }
+ ReplaceLastCorner(roundedCorner, spline);
+ }
+ spline.AddPoint(pt);
+}
+
PathTextContext::PathTextContext(m2::SharedSpline const & spline)
: m_globalSpline(spline)
{}
@@ -68,7 +167,7 @@ void PathTextContext::Update(ScreenBase const & screen)
}
continue;
}
- pixelSpline.AddPoint(screen.PtoP3d(pos));
+ AddPointAndRound(pixelSpline, screen.PtoP3d(pos));
}
if (pixelSpline.GetSize() > 1)
@@ -79,18 +178,10 @@ void PathTextContext::Update(ScreenBase const & screen)
for (size_t i = 0, sz = m_globalOffsets.size(); i < sz; ++i)
{
- if (screen.isPerspective())
- {
- m2::PointD const pt2d = screen.GtoP(m_globalPivots[i]);
- if (!screen.IsReverseProjection3d(pt2d))
- {
- m_centerPointIters.push_back(GetProjectedPoint(m_pixel3dSplines, screen.PtoP3d(pt2d)));
- m_centerGlobalPivots.push_back(m_globalPivots[i]);
- }
- }
- else
+ m2::PointD const pt2d = screen.GtoP(m_globalPivots[i]);
+ if (!screen.IsReverseProjection3d(pt2d))
{
- m_centerPointIters.push_back(m_pixel3dSplines.front().GetPoint(m_globalOffsets[i] / screen.GetScale()));
+ m_centerPointIters.push_back(GetProjectedPoint(m_pixel3dSplines, screen.PtoP3d(pt2d)));
m_centerGlobalPivots.push_back(m_globalPivots[i]);
}
}
diff --git a/drape_frontend/path_text_handle.hpp b/drape_frontend/path_text_handle.hpp
index d7a644ddaf..c6bf52f3dc 100644
--- a/drape_frontend/path_text_handle.hpp
+++ b/drape_frontend/path_text_handle.hpp
@@ -71,4 +71,7 @@ private:
m2::PointD m_globalPivot;
float const m_depth;
};
+
+bool IsValidSplineTurn(m2::PointD const & normalizedDir1, m2::PointD const & normalizedDir2);
+void AddPointAndRound(m2::Spline & spline, m2::PointD const & pt);
} // namespace df
diff --git a/drape_frontend/text_layout.cpp b/drape_frontend/text_layout.cpp
index 11df700bec..2cd9293f5e 100644
--- a/drape_frontend/text_layout.cpp
+++ b/drape_frontend/text_layout.cpp
@@ -439,7 +439,7 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, flo
penIter = endIter;
}
- glsl::vec2 pxPivot = glsl::ToVec2(iter.m_pos);
+ m2::PointD const pxPivot = iter.m_pos;
buffer.resize(4 * m_metrics.size());
glsl::vec4 const pivot(glsl::ToVec2(MapShape::ConvertToLocal(globalPivot, m_tileCenter,
@@ -447,14 +447,18 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, flo
for (size_t i = 0; i < m_metrics.size(); ++i)
{
GlyphRegion const & g = m_metrics[i];
- m2::PointF pxSize = g.GetPixelSize() * m_textSizeRatio;
+ m2::PointF const pxSize = g.GetPixelSize() * m_textSizeRatio;
+ float const xAdvance = g.GetAdvanceX() * m_textSizeRatio;
+
+ m2::PointD const baseVector = penIter.m_pos - pxPivot;
+ m2::PointD const currentTangent = penIter.m_avrDir.Normalize();
- m2::PointD const pxBase = penIter.m_pos;
- m2::PointD const pxShiftBase = penIter.m_pos + penIter.m_dir;
+ penIter.Advance(advanceSign * xAdvance);
+ m2::PointD const newTangent = penIter.m_avrDir.Normalize();
- glsl::vec2 tangent = advanceSign * glsl::normalize(glsl::ToVec2(pxShiftBase - pxBase));
- glsl::vec2 normal = glsl::normalize(glsl::vec2(-tangent.y, tangent.x));
- glsl::vec2 formingVector = (glsl::ToVec2(pxBase) - pxPivot) + (halfFontSize * normal);
+ glsl::vec2 tangent = glsl::ToVec2(newTangent);
+ glsl::vec2 normal = glsl::vec2(-tangent.y, tangent.x);
+ glsl::vec2 formingVector = glsl::ToVec2(baseVector) + halfFontSize * normal;
float const xOffset = g.GetOffsetX() * m_textSizeRatio;
float const yOffset = g.GetOffsetY() * m_textSizeRatio;
@@ -469,12 +473,12 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, flo
buffer[baseIndex + 2] = TDV(pivot, formingVector + normal * bottomVector + tangent * (pxSize.x + xOffset));
buffer[baseIndex + 3] = TDV(pivot, formingVector + normal * upVector + tangent * (pxSize.x + xOffset));
- float const xAdvance = g.GetAdvanceX() * m_textSizeRatio;
- glsl::vec2 currentTangent = glsl::ToVec2(penIter.m_dir);
- penIter.Advance(advanceSign * xAdvance);
- float const dotProduct = glsl::dot(currentTangent, glsl::ToVec2(penIter.m_dir));
- if (dotProduct < kValidSplineTurn)
- return false;
+ if (i > 0)
+ {
+ float const dotProduct = m2::DotProduct(currentTangent, newTangent);
+ if (dotProduct < kValidSplineTurn)
+ return false;
+ }
}
return true;
}