/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

  1. #include "drape_frontend/line_shape_helper.hpp"
  2. #include "drape/glsl_func.hpp"
  3. #include "base/assert.hpp"
  4. namespace df
  5. {
  6. namespace
  7. {
  8. void UpdateNormalBetweenSegments(LineSegment * segment1, LineSegment * segment2)
  9. {
  10. ASSERT(segment1 != nullptr && segment2 != nullptr, ());
  11. float const dotProduct = glsl::dot(segment1->m_leftNormals[EndPoint],
  12. segment2->m_leftNormals[StartPoint]);
  13. float const absDotProduct = fabs(dotProduct);
  14. float const kEps = 1e-5;
  15. if (fabs(absDotProduct - 1.0f) < kEps)
  16. {
  17. // change nothing
  18. return;
  19. }
  20. float const kMaxScalar = 5;
  21. float const crossProduct = glsl::cross(glsl::vec3(segment1->m_tangent, 0),
  22. glsl::vec3(segment2->m_tangent, 0)).z;
  23. if (crossProduct < 0)
  24. {
  25. segment1->m_hasLeftJoin[EndPoint] = true;
  26. segment2->m_hasLeftJoin[StartPoint] = true;
  27. // change right-side normals
  28. glsl::vec2 averageNormal = glsl::normalize(segment1->m_rightNormals[EndPoint] +
  29. segment2->m_rightNormals[StartPoint]);
  30. float const cosAngle = glsl::dot(segment1->m_tangent, averageNormal);
  31. float const widthScalar = 1.0f / sqrt(1.0f - cosAngle * cosAngle);
  32. if (widthScalar < kMaxScalar)
  33. {
  34. segment1->m_rightNormals[EndPoint] = averageNormal;
  35. segment2->m_rightNormals[StartPoint] = averageNormal;
  36. segment1->m_rightWidthScalar[EndPoint].x = widthScalar;
  37. segment1->m_rightWidthScalar[EndPoint].y = widthScalar * cosAngle;
  38. segment2->m_rightWidthScalar[StartPoint] = segment1->m_rightWidthScalar[EndPoint];
  39. }
  40. else
  41. {
  42. segment1->m_generateJoin = false;
  43. }
  44. }
  45. else
  46. {
  47. segment1->m_hasLeftJoin[EndPoint] = false;
  48. segment2->m_hasLeftJoin[StartPoint] = false;
  49. // change left-side normals
  50. glsl::vec2 averageNormal = glsl::normalize(segment1->m_leftNormals[EndPoint] +
  51. segment2->m_leftNormals[StartPoint]);
  52. float const cosAngle = glsl::dot(segment1->m_tangent, averageNormal);
  53. float const widthScalar = 1.0f / sqrt(1.0f - cosAngle * cosAngle);
  54. if (widthScalar < kMaxScalar)
  55. {
  56. segment1->m_leftNormals[EndPoint] = averageNormal;
  57. segment2->m_leftNormals[StartPoint] = averageNormal;
  58. segment1->m_leftWidthScalar[EndPoint].x = widthScalar;
  59. segment1->m_leftWidthScalar[EndPoint].y = widthScalar * cosAngle;
  60. segment2->m_leftWidthScalar[StartPoint] = segment1->m_leftWidthScalar[EndPoint];
  61. }
  62. else
  63. {
  64. segment1->m_generateJoin = false;
  65. }
  66. }
  67. }
  68. } // namespace
  69. void CalculateTangentAndNormals(glsl::vec2 const & pt0, glsl::vec2 const & pt1,
  70. glsl::vec2 & tangent, glsl::vec2 & leftNormal,
  71. glsl::vec2 & rightNormal)
  72. {
  73. tangent = glsl::normalize(pt1 - pt0);
  74. leftNormal = glsl::vec2(-tangent.y, tangent.x);
  75. rightNormal = -leftNormal;
  76. }
  77. void ConstructLineSegments(std::vector<m2::PointD> const & path, std::vector<glsl::vec4> const & segmentsColors,
  78. std::vector<LineSegment> & segments)
  79. {
  80. ASSERT_LESS(1, path.size(), ());
  81. if (!segmentsColors.empty())
  82. ASSERT_EQUAL(segmentsColors.size() + 1, path.size(), ());
  83. m2::PointD prevPoint = path[0];
  84. for (size_t i = 1; i < path.size(); ++i)
  85. {
  86. m2::PointF const p1 = m2::PointF(prevPoint.x, prevPoint.y);
  87. m2::PointF const p2 = m2::PointF(path[i].x, path[i].y);
  88. if (p1.EqualDxDy(p2, 1.0E-5))
  89. continue;
  90. // Important! Do emplace_back first and fill parameters later.
  91. // Fill parameters first and push_back later will cause ugly bug in clang 3.6 -O3 optimization.
  92. segments.emplace_back(glsl::ToVec2(p1), glsl::ToVec2(p2));
  93. LineSegment & segment = segments.back();
  94. CalculateTangentAndNormals(glsl::ToVec2(p1), glsl::ToVec2(p2), segment.m_tangent,
  95. segment.m_leftBaseNormal, segment.m_rightBaseNormal);
  96. segment.m_leftNormals[StartPoint] = segment.m_leftNormals[EndPoint] = segment.m_leftBaseNormal;
  97. segment.m_rightNormals[StartPoint] = segment.m_rightNormals[EndPoint] = segment.m_rightBaseNormal;
  98. if (!segmentsColors.empty())
  99. segment.m_color = segmentsColors[i - 1];
  100. prevPoint = path[i];
  101. }
  102. }
  103. void UpdateNormals(LineSegment * segment, LineSegment * prevSegment, LineSegment * nextSegment)
  104. {
  105. ASSERT(segment != nullptr, ());
  106. if (prevSegment != nullptr)
  107. UpdateNormalBetweenSegments(prevSegment, segment);
  108. if (nextSegment != nullptr)
  109. UpdateNormalBetweenSegments(segment, nextSegment);
  110. }
  111. void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
  112. float halfWidth, bool isLeft, float widthScalar, std::vector<glsl::vec2> & normals,
  113. std::vector<glsl::vec2> * uv)
  114. {
  115. float const eps = 1e-5;
  116. if (fabs(glsl::dot(normal1, normal2) - 1.0f) < eps)
  117. return;
  118. if (joinType == dp::LineJoin::BevelJoin)
  119. {
  120. glsl::vec2 const n1 = halfWidth * normal1;
  121. glsl::vec2 const n2 = halfWidth * normal2;
  122. normals.push_back(glsl::vec2(0.0f, 0.0f));
  123. normals.push_back(isLeft ? n1 : n2);
  124. normals.push_back(isLeft ? n2 : n1);
  125. if (uv != nullptr)
  126. {
  127. uv->push_back(glsl::vec2(0.5f, 0.5f));
  128. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  129. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  130. }
  131. }
  132. else if (joinType == dp::LineJoin::MiterJoin)
  133. {
  134. glsl::vec2 averageNormal = halfWidth * widthScalar * glsl::normalize(normal1 + normal2);
  135. glsl::vec2 const n1 = halfWidth * normal1;
  136. glsl::vec2 const n2 = halfWidth * normal2;
  137. normals.push_back(glsl::vec2(0.0f, 0.0f));
  138. normals.push_back(isLeft ? n1 : averageNormal);
  139. normals.push_back(isLeft ? averageNormal : n1);
  140. normals.push_back(glsl::vec2(0.0f, 0.0f));
  141. normals.push_back(isLeft ? averageNormal : n2);
  142. normals.push_back(isLeft ? n2 : averageNormal);
  143. if (uv != nullptr)
  144. {
  145. uv->push_back(glsl::vec2(0.5f, 0.5f));
  146. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  147. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  148. uv->push_back(glsl::vec2(0.5f, 0.5f));
  149. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  150. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  151. }
  152. }
  153. else
  154. {
  155. double const segmentAngle = math::pi / 8.0;
  156. double const fullAngle = acos(glsl::dot(normal1, normal2));
  157. int segmentsCount = static_cast<int>(fullAngle / segmentAngle);
  158. if (segmentsCount == 0)
  159. segmentsCount = 1;
  160. double const angle = fullAngle / segmentsCount * (isLeft ? -1.0 : 1.0);
  161. glsl::vec2 const normalizedNormal = glsl::normalize(normal1);
  162. m2::PointD const startNormal(normalizedNormal.x, normalizedNormal.y);
  163. for (int i = 0; i < segmentsCount; i++)
  164. {
  165. m2::PointD n1 = m2::Rotate(startNormal, i * angle) * halfWidth;
  166. m2::PointD n2 = m2::Rotate(startNormal, (i + 1) * angle) * halfWidth;
  167. normals.push_back(glsl::vec2(0.0f, 0.0f));
  168. normals.push_back(isLeft ? glsl::vec2(n1.x, n1.y) : glsl::vec2(n2.x, n2.y));
  169. normals.push_back(isLeft ? glsl::vec2(n2.x, n2.y) : glsl::vec2(n1.x, n1.y));
  170. if (uv != nullptr)
  171. {
  172. uv->push_back(glsl::vec2(0.5f, 0.5f));
  173. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  174. uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
  175. }
  176. }
  177. }
  178. }
  179. void GenerateCapNormals(dp::LineCap capType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
  180. glsl::vec2 const & direction, float halfWidth, bool isStart,
  181. std::vector<glsl::vec2> & normals, int segmentsCount)
  182. {
  183. if (capType == dp::ButtCap)
  184. return;
  185. if (capType == dp::SquareCap)
  186. {
  187. glsl::vec2 const n1 = halfWidth * normal1;
  188. glsl::vec2 const n2 = halfWidth * normal2;
  189. glsl::vec2 const n3 = halfWidth * (normal1 + direction);
  190. glsl::vec2 const n4 = halfWidth * (normal2 + direction);
  191. normals.push_back(n2);
  192. normals.push_back(isStart ? n4 : n1);
  193. normals.push_back(isStart ? n1 : n4);
  194. normals.push_back(n1);
  195. normals.push_back(isStart ? n4 : n3);
  196. normals.push_back(isStart ? n3 : n4);
  197. }
  198. else
  199. {
  200. double const segmentSize = math::pi / segmentsCount * (isStart ? -1.0 : 1.0);
  201. glsl::vec2 const normalizedNormal = glsl::normalize(normal2);
  202. m2::PointD const startNormal(normalizedNormal.x, normalizedNormal.y);
  203. for (int i = 0; i < segmentsCount; i++)
  204. {
  205. m2::PointD n1 = m2::Rotate(startNormal, i * segmentSize) * halfWidth;
  206. m2::PointD n2 = m2::Rotate(startNormal, (i + 1) * segmentSize) * halfWidth;
  207. normals.push_back(glsl::vec2(0.0f, 0.0f));
  208. normals.push_back(isStart ? glsl::vec2(n1.x, n1.y) : glsl::vec2(n2.x, n2.y));
  209. normals.push_back(isStart ? glsl::vec2(n2.x, n2.y) : glsl::vec2(n1.x, n1.y));
  210. }
  211. }
  212. }
  213. glsl::vec2 GetNormal(LineSegment const & segment, bool isLeft, ENormalType normalType)
  214. {
  215. if (normalType == BaseNormal)
  216. return isLeft ? segment.m_leftBaseNormal : segment.m_rightBaseNormal;
  217. int const index = (normalType == StartNormal) ? StartPoint : EndPoint;
  218. return isLeft ? segment.m_leftWidthScalar[index].x * segment.m_leftNormals[index]:
  219. segment.m_rightWidthScalar[index].x * segment.m_rightNormals[index];
  220. }
  221. float GetProjectionLength(glsl::vec2 const & newPoint, glsl::vec2 const & startPoint,
  222. glsl::vec2 const & endPoint)
  223. {
  224. glsl::vec2 const v1 = endPoint - startPoint;
  225. glsl::vec2 const v2 = newPoint - startPoint;
  226. float const squareLen = glsl::dot(v1, v1);
  227. float const proj = glsl::dot(v1, v2) / squareLen;
  228. return sqrt(squareLen) * base::Clamp(proj, 0.0f, 1.0f);
  229. }
  230. } // namespace df