Polyline.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. /**
  2. * Copyright (c) 2006-2023 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. // LOVE
  21. #include "Polyline.h"
  22. #include "graphics/Graphics.h"
  23. // C++
  24. #include <algorithm>
  25. // treat adjacent segments with angles between their directions <5 degree as straight
  26. static const float LINES_PARALLEL_EPS = 0.05f;
  27. namespace love
  28. {
  29. namespace graphics
  30. {
  31. void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
  32. {
  33. static std::vector<Vector2> anchors;
  34. anchors.clear();
  35. anchors.reserve(size_hint);
  36. static std::vector<Vector2> normals;
  37. normals.clear();
  38. normals.reserve(size_hint);
  39. // prepare vertex arrays
  40. if (draw_overdraw)
  41. halfwidth -= pixel_size * 0.3f;
  42. // compute sleeve
  43. bool is_looping = (coords[0] == coords[count - 1]);
  44. Vector2 segment;
  45. if (!is_looping) // virtual starting point at second point mirrored on first point
  46. segment = coords[1] - coords[0];
  47. else // virtual starting point at last vertex
  48. segment = coords[0] - coords[count - 2];
  49. float segmentLength = segment.getLength();
  50. Vector2 segmentNormal = segment.getNormal(halfwidth / segmentLength);
  51. Vector2 pointA, pointB(coords[0]);
  52. for (size_t i = 0; i + 1 < count; i++)
  53. {
  54. pointA = pointB;
  55. pointB = coords[i + 1];
  56. renderEdge(anchors, normals, segment, segmentLength, segmentNormal, pointA, pointB, halfwidth);
  57. }
  58. pointA = pointB;
  59. pointB = is_looping ? coords[1] : pointB + segment;
  60. renderEdge(anchors, normals, segment, segmentLength, segmentNormal, pointA, pointB, halfwidth);
  61. vertex_count = normals.size();
  62. size_t extra_vertices = 0;
  63. if (draw_overdraw)
  64. {
  65. calc_overdraw_vertex_count(is_looping);
  66. // When drawing overdraw lines using triangle strips, we want to add an
  67. // extra degenerate triangle in between the core line and the overdraw
  68. // line in order to break up the strip into two. This will let us draw
  69. // everything in one draw call.
  70. if (triangle_mode == vertex::TriangleIndexMode::STRIP)
  71. extra_vertices = 2;
  72. }
  73. // Use a single linear array for both the regular and overdraw vertices.
  74. vertices = new Vector2[vertex_count + extra_vertices + overdraw_vertex_count];
  75. for (size_t i = 0; i < vertex_count; ++i)
  76. vertices[i] = anchors[i] + normals[i];
  77. if (draw_overdraw)
  78. {
  79. overdraw = vertices + vertex_count + extra_vertices;
  80. overdraw_vertex_start = vertex_count + extra_vertices;
  81. render_overdraw(normals, pixel_size, is_looping);
  82. }
  83. // Add the degenerate triangle strip.
  84. if (extra_vertices)
  85. {
  86. vertices[vertex_count + 0] = vertices[vertex_count - 1];
  87. vertices[vertex_count + 1] = vertices[overdraw_vertex_start];
  88. }
  89. }
  90. void NoneJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
  91. Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
  92. const Vector2 &pointA, const Vector2 &pointB, float halfWidth)
  93. {
  94. // ns1------ns2
  95. // | |
  96. // q ------ r
  97. // | |
  98. // (-ns1)----(-ns2)
  99. anchors.push_back(pointA);
  100. anchors.push_back(pointA);
  101. normals.push_back(segmentNormal);
  102. normals.push_back(-segmentNormal);
  103. segment = (pointB - pointA);
  104. segmentLength = segment.getLength();
  105. segmentNormal = segment.getNormal(halfWidth / segmentLength);
  106. anchors.push_back(pointA);
  107. anchors.push_back(pointA);
  108. normals.push_back(segmentNormal);
  109. normals.push_back(-segmentNormal);
  110. }
  111. /** Calculate line boundary points.
  112. *
  113. * Sketch:
  114. *
  115. * u1
  116. * -------------+---...___
  117. * | ```'''-- ---
  118. * p- - - - - - q- - . _ _ | w/2
  119. * | ` ' ' r +
  120. * -------------+---...___ | w/2
  121. * u2 ```'''-- ---
  122. *
  123. * u1 and u2 depend on four things:
  124. * - the half line width w/2
  125. * - the previous line vertex p
  126. * - the current line vertex q
  127. * - the next line vertex r
  128. *
  129. * u1/u2 are the intersection points of the parallel lines to p-q and q-r,
  130. * i.e. the point where
  131. *
  132. * (q + w/2 * ns) + lambda * (q - p) = (q + w/2 * nt) + mu * (r - q) (u1)
  133. * (q - w/2 * ns) + lambda * (q - p) = (q - w/2 * nt) + mu * (r - q) (u2)
  134. *
  135. * with nt,nt being the normals on the segments s = p-q and t = q-r,
  136. *
  137. * ns = perp(s) / |s|
  138. * nt = perp(t) / |t|.
  139. *
  140. * Using the linear equation system (similar for u2)
  141. *
  142. * q + w/2 * ns + lambda * s - (q + w/2 * nt + mu * t) = 0 (u1)
  143. * <=> q-q + lambda * s - mu * t = (nt - ns) * w/2
  144. * <=> lambda * s - mu * t = (nt - ns) * w/2
  145. *
  146. * the intersection points can be efficiently calculated using Cramer's rule.
  147. */
  148. void MiterJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
  149. Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
  150. const Vector2 &pointA, const Vector2 &pointB, float halfwidth)
  151. {
  152. Vector2 newSegment = (pointB - pointA);
  153. float newSegmentLength = newSegment.getLength();
  154. if (newSegmentLength == 0.0f)
  155. {
  156. // degenerate segment, skip it
  157. return;
  158. }
  159. Vector2 newSegmentNormal = newSegment.getNormal(halfwidth / newSegmentLength);
  160. anchors.push_back(pointA);
  161. anchors.push_back(pointA);
  162. float det = Vector2::cross(segment, newSegment);
  163. if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS)
  164. {
  165. // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
  166. normals.push_back(segmentNormal);
  167. normals.push_back(-segmentNormal);
  168. if (Vector2::dot(segment, newSegment) < 0)
  169. {
  170. // line reverses direction; because the normal flips, the
  171. // triangle strip would twist here, so insert a zero-size
  172. // quad to contain the twist
  173. // ____.___.____
  174. // | |\ /| |
  175. // p q X q r
  176. // |____|/ \|____|
  177. anchors.push_back(pointA);
  178. anchors.push_back(pointA);
  179. normals.push_back(-segmentNormal);
  180. normals.push_back(segmentNormal);
  181. }
  182. }
  183. else
  184. {
  185. // cramers rule
  186. float lambda = Vector2::cross((newSegmentNormal - segmentNormal), newSegment) / det;
  187. Vector2 d = segmentNormal + segment * lambda;
  188. normals.push_back(d);
  189. normals.push_back(-d);
  190. }
  191. segment = newSegment;
  192. segmentNormal = newSegmentNormal;
  193. segmentLength = newSegmentLength;
  194. }
  195. /** Calculate line boundary points.
  196. *
  197. * Sketch:
  198. *
  199. * uh1___uh2
  200. * .' '.
  201. * .' q '.
  202. * .' ' ' '.
  203. *.' ' .'. ' '.
  204. * ' .' ul'. '
  205. * p .' '. r
  206. *
  207. *
  208. * ul can be found as above, uh1 and uh2 are much simpler:
  209. *
  210. * uh1 = q + ns * w/2, uh2 = q + nt * w/2
  211. */
  212. void BevelJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
  213. Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
  214. const Vector2 &pointA, const Vector2 &pointB, float halfWidth)
  215. {
  216. Vector2 newSegment = (pointB - pointA);
  217. float newSegmentLength = newSegment.getLength();
  218. float det = Vector2::cross(segment, newSegment);
  219. if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS)
  220. {
  221. // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
  222. Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength);
  223. anchors.push_back(pointA);
  224. anchors.push_back(pointA);
  225. normals.push_back(segmentNormal);
  226. normals.push_back(-segmentNormal);
  227. if (Vector2::dot(segment, newSegment) < 0)
  228. {
  229. // line reverses direction; same as for miter
  230. anchors.push_back(pointA);
  231. anchors.push_back(pointA);
  232. normals.push_back(-segmentNormal);
  233. normals.push_back(segmentNormal);
  234. }
  235. segment = newSegment;
  236. segmentLength = newSegmentLength;
  237. segmentNormal = newSegmentNormal;
  238. return; // early out
  239. }
  240. // cramers rule
  241. Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength);
  242. float lambda = Vector2::cross((newSegmentNormal - segmentNormal), newSegment) / det;
  243. Vector2 d = segmentNormal + segment * lambda;
  244. anchors.push_back(pointA);
  245. anchors.push_back(pointA);
  246. anchors.push_back(pointA);
  247. anchors.push_back(pointA);
  248. if (det > 0) // 'left' turn -> intersection on the top
  249. {
  250. normals.push_back(d);
  251. normals.push_back(-segmentNormal);
  252. normals.push_back(d);
  253. normals.push_back(-newSegmentNormal);
  254. }
  255. else
  256. {
  257. normals.push_back(segmentNormal);
  258. normals.push_back(-d);
  259. normals.push_back(newSegmentNormal);
  260. normals.push_back(-d);
  261. }
  262. segment = newSegment;
  263. segmentLength = newSegmentLength;
  264. segmentNormal = newSegmentNormal;
  265. }
  266. void Polyline::calc_overdraw_vertex_count(bool is_looping)
  267. {
  268. overdraw_vertex_count = 2 * vertex_count + (is_looping ? 0 : 2);
  269. }
  270. void Polyline::render_overdraw(const std::vector<Vector2> &normals, float pixel_size, bool is_looping)
  271. {
  272. // upper segment
  273. for (size_t i = 0; i + 1 < vertex_count; i += 2)
  274. {
  275. overdraw[i] = vertices[i];
  276. overdraw[i+1] = vertices[i] + normals[i] * (pixel_size / normals[i].getLength());
  277. }
  278. // lower segment
  279. for (size_t i = 0; i + 1 < vertex_count; i += 2)
  280. {
  281. size_t k = vertex_count - i - 1;
  282. overdraw[vertex_count + i] = vertices[k];
  283. overdraw[vertex_count + i+1] = vertices[k] + normals[k] * (pixel_size / normals[k].getLength());
  284. }
  285. // if not looping, the outer overdraw vertices need to be displaced
  286. // to cover the line endings, i.e.:
  287. // +- - - - //- - + +- - - - - //- - - +
  288. // +-------//-----+ : +-------//-----+ :
  289. // | core // line | --> : | core // line | :
  290. // +-----//-------+ : +-----//-------+ :
  291. // +- - //- - - - + +- - - //- - - - - +
  292. if (!is_looping)
  293. {
  294. // left edge
  295. Vector2 spacer = (overdraw[1] - overdraw[3]);
  296. spacer.normalize(pixel_size);
  297. overdraw[1] += spacer;
  298. overdraw[overdraw_vertex_count - 3] += spacer;
  299. // right edge
  300. spacer = (overdraw[vertex_count-1] - overdraw[vertex_count-3]);
  301. spacer.normalize(pixel_size);
  302. overdraw[vertex_count-1] += spacer;
  303. overdraw[vertex_count+1] += spacer;
  304. // we need to draw two more triangles to close the
  305. // overdraw at the line start.
  306. overdraw[overdraw_vertex_count-2] = overdraw[0];
  307. overdraw[overdraw_vertex_count-1] = overdraw[1];
  308. }
  309. }
  310. void NoneJoinPolyline::calc_overdraw_vertex_count(bool /*is_looping*/)
  311. {
  312. overdraw_vertex_count = 4 * (vertex_count-2); // less than ideal
  313. }
  314. void NoneJoinPolyline::render_overdraw(const std::vector<Vector2> &/*normals*/, float pixel_size, bool /*is_looping*/)
  315. {
  316. for (size_t i = 2; i + 3 < vertex_count; i += 4)
  317. {
  318. // v0-v2
  319. // | / | <- main quad line
  320. // v1-v3
  321. Vector2 s = vertices[i+0] - vertices[i+2];
  322. Vector2 t = vertices[i+0] - vertices[i+1];
  323. s.normalize(pixel_size);
  324. t.normalize(pixel_size);
  325. const size_t k = 4 * (i - 2);
  326. overdraw[k+0] = vertices[i+0];
  327. overdraw[k+1] = vertices[i+1];
  328. overdraw[k+2] = vertices[i+0] + s + t;
  329. overdraw[k+3] = vertices[i+1] + s - t;
  330. overdraw[k+4] = vertices[i+1];
  331. overdraw[k+5] = vertices[i+3];
  332. overdraw[k+6] = vertices[i+1] + s - t;
  333. overdraw[k+7] = vertices[i+3] - s - t;
  334. overdraw[k+ 8] = vertices[i+3];
  335. overdraw[k+ 9] = vertices[i+2];
  336. overdraw[k+10] = vertices[i+3] - s - t;
  337. overdraw[k+11] = vertices[i+2] - s + t;
  338. overdraw[k+12] = vertices[i+2];
  339. overdraw[k+13] = vertices[i+0];
  340. overdraw[k+14] = vertices[i+2] - s + t;
  341. overdraw[k+15] = vertices[i+0] + s + t;
  342. }
  343. }
  344. Polyline::~Polyline()
  345. {
  346. if (vertices)
  347. delete[] vertices;
  348. }
  349. void Polyline::draw(love::graphics::Graphics *gfx)
  350. {
  351. const Matrix4 &t = gfx->getTransform();
  352. bool is2D = t.isAffine2DTransform();
  353. Color32 curcolor = toColor32(gfx->getColor());
  354. int overdraw_start = (int) overdraw_vertex_start;
  355. int overdraw_count = (int) overdraw_vertex_count;
  356. int total_vertex_count = (int) vertex_count;
  357. if (overdraw)
  358. total_vertex_count = overdraw_start + overdraw_count;
  359. // love's automatic batching can only deal with < 65k vertices per draw.
  360. // uint16_max - 3 is evenly divisible by 6 (needed for quads mode).
  361. int maxvertices = LOVE_UINT16_MAX - 3;
  362. int advance = maxvertices;
  363. if (triangle_mode == vertex::TriangleIndexMode::STRIP)
  364. advance -= 2;
  365. for (int vertex_start = 0; vertex_start < total_vertex_count; vertex_start += advance)
  366. {
  367. const Vector2 *verts = vertices + vertex_start;
  368. Graphics::StreamDrawCommand cmd;
  369. cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
  370. cmd.formats[1] = vertex::CommonFormat::RGBAub;
  371. cmd.indexMode = triangle_mode;
  372. cmd.vertexCount = std::min(maxvertices, total_vertex_count - vertex_start);
  373. Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
  374. if (is2D)
  375. t.transformXY((Vector2 *) data.stream[0], verts, cmd.vertexCount);
  376. else
  377. t.transformXY0((Vector3 *) data.stream[0], verts, cmd.vertexCount);
  378. Color32 *colordata = (Color32 *) data.stream[1];
  379. int draw_rough_count = std::min(cmd.vertexCount, (int) vertex_count - vertex_start);
  380. // Constant vertex color up to the overdraw vertices.
  381. for (int i = 0; i < draw_rough_count; i++)
  382. colordata[i] = curcolor;
  383. if (overdraw)
  384. {
  385. int draw_remaining_count = cmd.vertexCount - draw_rough_count;
  386. int draw_overdraw_begin = overdraw_start - vertex_start;
  387. int draw_overdraw_end = draw_overdraw_begin + overdraw_count;
  388. draw_overdraw_begin = std::max(0, draw_overdraw_begin);
  389. int draw_overdraw_count = std::min(draw_remaining_count, draw_overdraw_end - draw_overdraw_begin);
  390. if (draw_overdraw_count > 0)
  391. {
  392. Color32 *colors = colordata + draw_overdraw_begin;
  393. fill_color_array(curcolor, colors, draw_overdraw_count);
  394. }
  395. }
  396. }
  397. }
  398. void Polyline::fill_color_array(Color32 constant_color, Color32 *colors, int count)
  399. {
  400. for (int i = 0; i < count; ++i)
  401. {
  402. Color32 c = constant_color;
  403. c.a *= (i+1) % 2; // avoids branching. equiv to if (i%2 == 1) c.a = 0;
  404. colors[i] = c;
  405. }
  406. }
  407. void NoneJoinPolyline::fill_color_array(Color32 constant_color, Color32 *colors, int count)
  408. {
  409. for (int i = 0; i < count; ++i)
  410. {
  411. Color32 c = constant_color;
  412. c.a *= (i & 3) < 2; // if (i % 4 == 2 || i % 4 == 3) c.a = 0
  413. colors[i] = c;
  414. }
  415. }
  416. } // graphics
  417. } // love