Polyline.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /**
  2. * Copyright (c) 2006-2013 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. #include <algorithm>
  21. // LOVE
  22. #include "Polyline.h"
  23. // OpenGL
  24. #include "OpenGL.h"
  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. namespace opengl
  32. {
  33. void Polyline::render(const float *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
  34. {
  35. static std::vector<Vector> anchors;
  36. anchors.clear();
  37. anchors.reserve(size_hint);
  38. static std::vector<Vector> normals;
  39. normals.clear();
  40. normals.reserve(size_hint);
  41. // prepare vertex arrays
  42. if (draw_overdraw)
  43. halfwidth -= pixel_size * .3;
  44. // compute sleeve
  45. bool is_looping = (coords[0] == coords[count - 2]) && (coords[1] == coords[count - 1]);
  46. Vector s;
  47. if (!is_looping) // virtual starting point at second point mirrored on first point
  48. s = Vector(coords[2] - coords[0], coords[3] - coords[1]);
  49. else // virtual starting point at last vertex
  50. s = Vector(coords[0] - coords[count - 4], coords[1] - coords[count - 3]);
  51. float len_s = s.getLength();
  52. Vector ns = s.getNormal(halfwidth / len_s);
  53. Vector q, r(coords[0], coords[1]);
  54. for (size_t i = 0; i + 3 < count; i += 2)
  55. {
  56. q = r;
  57. r = Vector(coords[i + 2], coords[i + 3]);
  58. renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
  59. }
  60. q = r;
  61. r = is_looping ? Vector(coords[2], coords[3]) : r + s;
  62. renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
  63. vertex_count = normals.size();
  64. vertices = new Vector[vertex_count];
  65. for (size_t i = 0; i < vertex_count; ++i)
  66. vertices[i] = anchors[i] + normals[i];
  67. if (draw_overdraw)
  68. render_overdraw(normals, pixel_size, is_looping);
  69. }
  70. void NoneJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
  71. Vector &s, float &len_s, Vector &ns,
  72. const Vector &q, const Vector &r, float hw)
  73. {
  74. anchors.push_back(q);
  75. anchors.push_back(q);
  76. normals.push_back(ns);
  77. normals.push_back(-ns);
  78. s = (r - q);
  79. len_s = s.getLength();
  80. ns = s.getNormal(hw / len_s);
  81. anchors.push_back(q);
  82. anchors.push_back(q);
  83. normals.push_back(-ns);
  84. normals.push_back(ns);
  85. }
  86. /** Calculate line boundary points.
  87. *
  88. * Sketch:
  89. *
  90. * u1
  91. * -------------+---...___
  92. * | ```'''-- ---
  93. * p- - - - - - q- - . _ _ | w/2
  94. * | ` ' ' r +
  95. * -------------+---...___ | w/2
  96. * u2 ```'''-- ---
  97. *
  98. * u1 and u2 depend on four things:
  99. * - the half line width w/2
  100. * - the previous line vertex p
  101. * - the current line vertex q
  102. * - the next line vertex r
  103. *
  104. * u1/u2 are the intersection points of the parallel lines to p-q and q-r,
  105. * i.e. the point where
  106. *
  107. * (q + w/2 * ns) + lambda * (q - p) = (q + w/2 * nt) + mu * (r - q) (u1)
  108. * (q - w/2 * ns) + lambda * (q - p) = (q - w/2 * nt) + mu * (r - q) (u2)
  109. *
  110. * with nt,nt being the normals on the segments s = p-q and t = q-r,
  111. *
  112. * ns = perp(s) / |s|
  113. * nt = perp(t) / |t|.
  114. *
  115. * Using the linear equation system (similar for u2)
  116. *
  117. * q + w/2 * ns + lambda * s - (q + w/2 * nt + mu * t) = 0 (u1)
  118. * <=> q-q + lambda * s - mu * t = (nt - ns) * w/2
  119. * <=> lambda * s - mu * t = (nt - ns) * w/2
  120. *
  121. * the intersection points can be efficiently calculated using Cramer's rule.
  122. */
  123. void MiterJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
  124. Vector &s, float &len_s, Vector &ns,
  125. const Vector &q, const Vector &r, float hw)
  126. {
  127. Vector t = (r - q);
  128. float len_t = t.getLength();
  129. Vector nt = t.getNormal(hw / len_t);
  130. anchors.push_back(q);
  131. anchors.push_back(q);
  132. float det = s ^ t;
  133. if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && s * t > 0)
  134. {
  135. // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
  136. normals.push_back(ns);
  137. normals.push_back(-ns);
  138. }
  139. else
  140. {
  141. // cramers rule
  142. float lambda = ((nt - ns) ^ t) / det;
  143. Vector d = ns + s * lambda;
  144. normals.push_back(d);
  145. normals.push_back(-d);
  146. }
  147. s = t;
  148. ns = nt;
  149. len_s = len_t;
  150. }
  151. /** Calculate line boundary points.
  152. *
  153. * Sketch:
  154. *
  155. * uh1___uh2
  156. * .' '.
  157. * .' q '.
  158. * .' ' ' '.
  159. *.' ' .'. ' '.
  160. * ' .' ul'. '
  161. * p .' '. r
  162. *
  163. *
  164. * ul can be found as above, uh1 and uh2 are much simpler:
  165. *
  166. * uh1 = q + ns * w/2, uh2 = q + nt * w/2
  167. */
  168. void BevelJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
  169. Vector &s, float &len_s, Vector &ns,
  170. const Vector &q, const Vector &r, float hw)
  171. {
  172. Vector t = (r - q);
  173. float len_t = t.getLength();
  174. float det = s ^ t;
  175. if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && s * t > 0)
  176. {
  177. // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
  178. Vector n = t.getNormal(hw / len_t);
  179. anchors.push_back(q);
  180. anchors.push_back(q);
  181. normals.push_back(n);
  182. normals.push_back(-n);
  183. s = t;
  184. len_s = len_t;
  185. return; // early out
  186. }
  187. // cramers rule
  188. Vector nt= t.getNormal(hw / len_t);
  189. float lambda = ((nt - ns) ^ t) / det;
  190. Vector d = ns + s * lambda;
  191. anchors.push_back(q);
  192. anchors.push_back(q);
  193. anchors.push_back(q);
  194. anchors.push_back(q);
  195. if (det > 0) // 'left' turn -> intersection on the top
  196. {
  197. normals.push_back(d);
  198. normals.push_back(-ns);
  199. normals.push_back(d);
  200. normals.push_back(-nt);
  201. }
  202. else
  203. {
  204. normals.push_back(ns);
  205. normals.push_back(-d);
  206. normals.push_back(nt);
  207. normals.push_back(-d);
  208. }
  209. s = t;
  210. len_s = len_t;
  211. ns = nt;
  212. }
  213. void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
  214. {
  215. overdraw_vertex_count = 2 * vertex_count + (is_looping ? 0 : 2);
  216. overdraw = new Vector[overdraw_vertex_count];
  217. // upper segment
  218. for (size_t i = 0; i + 1 < vertex_count; i += 2)
  219. {
  220. overdraw[i] = vertices[i];
  221. overdraw[i+1] = vertices[i] + normals[i] * (pixel_size / normals[i].getLength());
  222. }
  223. // lower segment
  224. for (size_t i = 0; i + 1 < vertex_count; i += 2)
  225. {
  226. size_t k = vertex_count - i - 1;
  227. overdraw[vertex_count + i] = vertices[k];
  228. overdraw[vertex_count + i+1] = vertices[k] + normals[k] * (pixel_size / normals[i].getLength());
  229. }
  230. // if not looping, the outer overdraw vertices need to be displaced
  231. // to cover the line endings, i.e.:
  232. // +- - - - //- - + +- - - - - //- - - +
  233. // +-------//-----+ : +-------//-----+ :
  234. // | core // line | --> : | core // line | :
  235. // +-----//-------+ : +-----//-------+ :
  236. // +- - //- - - - + +- - - //- - - - - +
  237. if (!is_looping)
  238. {
  239. // left edge
  240. Vector spacer = (overdraw[1] - overdraw[3]);
  241. spacer.normalize(pixel_size);
  242. overdraw[1] += spacer;
  243. overdraw[overdraw_vertex_count - 3] += spacer;
  244. // right edge
  245. spacer = (overdraw[vertex_count-1] - overdraw[vertex_count-3]);
  246. spacer.normalize(pixel_size);
  247. overdraw[vertex_count-1] += spacer;
  248. overdraw[vertex_count+1] += spacer;
  249. // we need to draw two more triangles to close the
  250. // overdraw at the line start.
  251. overdraw[overdraw_vertex_count-2] = overdraw[0];
  252. overdraw[overdraw_vertex_count-1] = overdraw[1];
  253. }
  254. }
  255. void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
  256. {
  257. overdraw_vertex_count = 4 * (vertex_count-2); // less than ideal
  258. overdraw = new Vector[overdraw_vertex_count];
  259. for (size_t i = 2; i + 3 < vertex_count; i += 4)
  260. {
  261. Vector s = vertices[i] - vertices[i+3];
  262. Vector t = vertices[i] - vertices[i+1];
  263. s.normalize(pixel_size);
  264. t.normalize(pixel_size);
  265. const size_t k = 4 * (i - 2);
  266. overdraw[k ] = vertices[i];
  267. overdraw[k+1] = vertices[i] + s + t;
  268. overdraw[k+2] = vertices[i+1] + s - t;
  269. overdraw[k+3] = vertices[i+1];
  270. overdraw[k+4] = vertices[i+1];
  271. overdraw[k+5] = vertices[i+1] + s - t;
  272. overdraw[k+6] = vertices[i+2] - s - t;
  273. overdraw[k+7] = vertices[i+2];
  274. overdraw[k+8] = vertices[i+2];
  275. overdraw[k+9] = vertices[i+2] - s - t;
  276. overdraw[k+10] = vertices[i+3] - s + t;
  277. overdraw[k+11] = vertices[i+3];
  278. overdraw[k+12] = vertices[i+3];
  279. overdraw[k+13] = vertices[i+3] - s + t;
  280. overdraw[k+14] = vertices[i] + s + t;
  281. overdraw[k+15] = vertices[i];
  282. }
  283. }
  284. Polyline::~Polyline()
  285. {
  286. if (vertices)
  287. delete[] vertices;
  288. if (overdraw)
  289. delete[] overdraw;
  290. }
  291. void Polyline::draw()
  292. {
  293. gl.prepareDraw();
  294. // draw the core line
  295. gl.bindTexture(0);
  296. glEnableClientState(GL_VERTEX_ARRAY);
  297. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)vertices);
  298. glDrawArrays(draw_mode, 0, vertex_count);
  299. if (overdraw)
  300. {
  301. // prepare colors:
  302. Color c = gl.getColor();
  303. Color *colors = new Color[overdraw_vertex_count];
  304. fill_color_array(colors, c);
  305. glEnableClientState(GL_COLOR_ARRAY);
  306. glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
  307. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)overdraw);
  308. glDrawArrays(draw_mode, 0, overdraw_vertex_count);
  309. glDisableClientState(GL_COLOR_ARRAY);
  310. delete[] colors;
  311. gl.setColor(c);
  312. }
  313. glDisableClientState(GL_VERTEX_ARRAY);
  314. }
  315. void Polyline::fill_color_array(Color *colors, const Color &c)
  316. {
  317. for (size_t i = 0; i < overdraw_vertex_count; ++i)
  318. {
  319. colors[i] = c;
  320. // avoids branching. equiv to if (i%2 == 1) colors[i].a = 0;
  321. colors[i].a *= GLubyte((i+1) % 2);
  322. }
  323. }
  324. void NoneJoinPolyline::fill_color_array(Color *colors, const Color &c)
  325. {
  326. for (size_t i = 0; i < overdraw_vertex_count; ++i)
  327. {
  328. colors[i] = c;
  329. // if (i % 4 == 1 || i % 4 == 2) colors[i].a = 0
  330. colors[i].a *= GLubyte((i+1) % 4 < 2);
  331. }
  332. }
  333. } // opengl
  334. } // graphics
  335. } // love