/drape_frontend/line_shape_helper.cpp
https://github.com/mapsme/omim · C++ · 272 lines · 225 code · 42 blank · 5 comment · 39 complexity · 262739223a06d9ebbeb0fe870a0471d4 MD5 · raw file
- #include "drape_frontend/line_shape_helper.hpp"
- #include "drape/glsl_func.hpp"
- #include "base/assert.hpp"
- namespace df
- {
- namespace
- {
- void UpdateNormalBetweenSegments(LineSegment * segment1, LineSegment * segment2)
- {
- ASSERT(segment1 != nullptr && segment2 != nullptr, ());
- float const dotProduct = glsl::dot(segment1->m_leftNormals[EndPoint],
- segment2->m_leftNormals[StartPoint]);
- float const absDotProduct = fabs(dotProduct);
- float const kEps = 1e-5;
- if (fabs(absDotProduct - 1.0f) < kEps)
- {
- // change nothing
- return;
- }
- float const kMaxScalar = 5;
- float const crossProduct = glsl::cross(glsl::vec3(segment1->m_tangent, 0),
- glsl::vec3(segment2->m_tangent, 0)).z;
- if (crossProduct < 0)
- {
- segment1->m_hasLeftJoin[EndPoint] = true;
- segment2->m_hasLeftJoin[StartPoint] = true;
- // change right-side normals
- glsl::vec2 averageNormal = glsl::normalize(segment1->m_rightNormals[EndPoint] +
- segment2->m_rightNormals[StartPoint]);
- float const cosAngle = glsl::dot(segment1->m_tangent, averageNormal);
- float const widthScalar = 1.0f / sqrt(1.0f - cosAngle * cosAngle);
- if (widthScalar < kMaxScalar)
- {
- segment1->m_rightNormals[EndPoint] = averageNormal;
- segment2->m_rightNormals[StartPoint] = averageNormal;
- segment1->m_rightWidthScalar[EndPoint].x = widthScalar;
- segment1->m_rightWidthScalar[EndPoint].y = widthScalar * cosAngle;
- segment2->m_rightWidthScalar[StartPoint] = segment1->m_rightWidthScalar[EndPoint];
- }
- else
- {
- segment1->m_generateJoin = false;
- }
- }
- else
- {
- segment1->m_hasLeftJoin[EndPoint] = false;
- segment2->m_hasLeftJoin[StartPoint] = false;
- // change left-side normals
- glsl::vec2 averageNormal = glsl::normalize(segment1->m_leftNormals[EndPoint] +
- segment2->m_leftNormals[StartPoint]);
- float const cosAngle = glsl::dot(segment1->m_tangent, averageNormal);
- float const widthScalar = 1.0f / sqrt(1.0f - cosAngle * cosAngle);
- if (widthScalar < kMaxScalar)
- {
- segment1->m_leftNormals[EndPoint] = averageNormal;
- segment2->m_leftNormals[StartPoint] = averageNormal;
- segment1->m_leftWidthScalar[EndPoint].x = widthScalar;
- segment1->m_leftWidthScalar[EndPoint].y = widthScalar * cosAngle;
- segment2->m_leftWidthScalar[StartPoint] = segment1->m_leftWidthScalar[EndPoint];
- }
- else
- {
- segment1->m_generateJoin = false;
- }
- }
- }
- } // namespace
- void CalculateTangentAndNormals(glsl::vec2 const & pt0, glsl::vec2 const & pt1,
- glsl::vec2 & tangent, glsl::vec2 & leftNormal,
- glsl::vec2 & rightNormal)
- {
- tangent = glsl::normalize(pt1 - pt0);
- leftNormal = glsl::vec2(-tangent.y, tangent.x);
- rightNormal = -leftNormal;
- }
- void ConstructLineSegments(std::vector<m2::PointD> const & path, std::vector<glsl::vec4> const & segmentsColors,
- std::vector<LineSegment> & segments)
- {
- ASSERT_LESS(1, path.size(), ());
- if (!segmentsColors.empty())
- ASSERT_EQUAL(segmentsColors.size() + 1, path.size(), ());
- m2::PointD prevPoint = path[0];
- for (size_t i = 1; i < path.size(); ++i)
- {
- m2::PointF const p1 = m2::PointF(prevPoint.x, prevPoint.y);
- m2::PointF const p2 = m2::PointF(path[i].x, path[i].y);
- if (p1.EqualDxDy(p2, 1.0E-5))
- continue;
- // Important! Do emplace_back first and fill parameters later.
- // Fill parameters first and push_back later will cause ugly bug in clang 3.6 -O3 optimization.
- segments.emplace_back(glsl::ToVec2(p1), glsl::ToVec2(p2));
- LineSegment & segment = segments.back();
- CalculateTangentAndNormals(glsl::ToVec2(p1), glsl::ToVec2(p2), segment.m_tangent,
- segment.m_leftBaseNormal, segment.m_rightBaseNormal);
- segment.m_leftNormals[StartPoint] = segment.m_leftNormals[EndPoint] = segment.m_leftBaseNormal;
- segment.m_rightNormals[StartPoint] = segment.m_rightNormals[EndPoint] = segment.m_rightBaseNormal;
- if (!segmentsColors.empty())
- segment.m_color = segmentsColors[i - 1];
- prevPoint = path[i];
- }
- }
- void UpdateNormals(LineSegment * segment, LineSegment * prevSegment, LineSegment * nextSegment)
- {
- ASSERT(segment != nullptr, ());
- if (prevSegment != nullptr)
- UpdateNormalBetweenSegments(prevSegment, segment);
- if (nextSegment != nullptr)
- UpdateNormalBetweenSegments(segment, nextSegment);
- }
- void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
- float halfWidth, bool isLeft, float widthScalar, std::vector<glsl::vec2> & normals,
- std::vector<glsl::vec2> * uv)
- {
- float const eps = 1e-5;
- if (fabs(glsl::dot(normal1, normal2) - 1.0f) < eps)
- return;
- if (joinType == dp::LineJoin::BevelJoin)
- {
- glsl::vec2 const n1 = halfWidth * normal1;
- glsl::vec2 const n2 = halfWidth * normal2;
- normals.push_back(glsl::vec2(0.0f, 0.0f));
- normals.push_back(isLeft ? n1 : n2);
- normals.push_back(isLeft ? n2 : n1);
- if (uv != nullptr)
- {
- uv->push_back(glsl::vec2(0.5f, 0.5f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- }
- }
- else if (joinType == dp::LineJoin::MiterJoin)
- {
- glsl::vec2 averageNormal = halfWidth * widthScalar * glsl::normalize(normal1 + normal2);
- glsl::vec2 const n1 = halfWidth * normal1;
- glsl::vec2 const n2 = halfWidth * normal2;
- normals.push_back(glsl::vec2(0.0f, 0.0f));
- normals.push_back(isLeft ? n1 : averageNormal);
- normals.push_back(isLeft ? averageNormal : n1);
- normals.push_back(glsl::vec2(0.0f, 0.0f));
- normals.push_back(isLeft ? averageNormal : n2);
- normals.push_back(isLeft ? n2 : averageNormal);
- if (uv != nullptr)
- {
- uv->push_back(glsl::vec2(0.5f, 0.5f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- uv->push_back(glsl::vec2(0.5f, 0.5f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- }
- }
- else
- {
- double const segmentAngle = math::pi / 8.0;
- double const fullAngle = acos(glsl::dot(normal1, normal2));
- int segmentsCount = static_cast<int>(fullAngle / segmentAngle);
- if (segmentsCount == 0)
- segmentsCount = 1;
- double const angle = fullAngle / segmentsCount * (isLeft ? -1.0 : 1.0);
- glsl::vec2 const normalizedNormal = glsl::normalize(normal1);
- m2::PointD const startNormal(normalizedNormal.x, normalizedNormal.y);
- for (int i = 0; i < segmentsCount; i++)
- {
- m2::PointD n1 = m2::Rotate(startNormal, i * angle) * halfWidth;
- m2::PointD n2 = m2::Rotate(startNormal, (i + 1) * angle) * halfWidth;
- normals.push_back(glsl::vec2(0.0f, 0.0f));
- normals.push_back(isLeft ? glsl::vec2(n1.x, n1.y) : glsl::vec2(n2.x, n2.y));
- normals.push_back(isLeft ? glsl::vec2(n2.x, n2.y) : glsl::vec2(n1.x, n1.y));
- if (uv != nullptr)
- {
- uv->push_back(glsl::vec2(0.5f, 0.5f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
- }
- }
- }
- }
- void GenerateCapNormals(dp::LineCap capType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
- glsl::vec2 const & direction, float halfWidth, bool isStart,
- std::vector<glsl::vec2> & normals, int segmentsCount)
- {
- if (capType == dp::ButtCap)
- return;
- if (capType == dp::SquareCap)
- {
- glsl::vec2 const n1 = halfWidth * normal1;
- glsl::vec2 const n2 = halfWidth * normal2;
- glsl::vec2 const n3 = halfWidth * (normal1 + direction);
- glsl::vec2 const n4 = halfWidth * (normal2 + direction);
- normals.push_back(n2);
- normals.push_back(isStart ? n4 : n1);
- normals.push_back(isStart ? n1 : n4);
- normals.push_back(n1);
- normals.push_back(isStart ? n4 : n3);
- normals.push_back(isStart ? n3 : n4);
- }
- else
- {
- double const segmentSize = math::pi / segmentsCount * (isStart ? -1.0 : 1.0);
- glsl::vec2 const normalizedNormal = glsl::normalize(normal2);
- m2::PointD const startNormal(normalizedNormal.x, normalizedNormal.y);
- for (int i = 0; i < segmentsCount; i++)
- {
- m2::PointD n1 = m2::Rotate(startNormal, i * segmentSize) * halfWidth;
- m2::PointD n2 = m2::Rotate(startNormal, (i + 1) * segmentSize) * halfWidth;
- normals.push_back(glsl::vec2(0.0f, 0.0f));
- normals.push_back(isStart ? glsl::vec2(n1.x, n1.y) : glsl::vec2(n2.x, n2.y));
- normals.push_back(isStart ? glsl::vec2(n2.x, n2.y) : glsl::vec2(n1.x, n1.y));
- }
- }
- }
- glsl::vec2 GetNormal(LineSegment const & segment, bool isLeft, ENormalType normalType)
- {
- if (normalType == BaseNormal)
- return isLeft ? segment.m_leftBaseNormal : segment.m_rightBaseNormal;
- int const index = (normalType == StartNormal) ? StartPoint : EndPoint;
- return isLeft ? segment.m_leftWidthScalar[index].x * segment.m_leftNormals[index]:
- segment.m_rightWidthScalar[index].x * segment.m_rightNormals[index];
- }
- float GetProjectionLength(glsl::vec2 const & newPoint, glsl::vec2 const & startPoint,
- glsl::vec2 const & endPoint)
- {
- glsl::vec2 const v1 = endPoint - startPoint;
- glsl::vec2 const v2 = newPoint - startPoint;
- float const squareLen = glsl::dot(v1, v1);
- float const proj = glsl::dot(v1, v2) / squareLen;
- return sqrt(squareLen) * base::Clamp(proj, 0.0f, 1.0f);
- }
- } // namespace df