gs_vg.h 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. /*==============================================================================================================
  2. * Copyright (c) 2020 John Jackson
  3. * GSPhysics: Simple vector graphics rendering
  4. * File: gs_vg.h
  5. * Github: https://github.com/MrFrenik/gunslinger
  6. * All Rights Reserved
  7. * MIT License
  8. * May all those that this source may reach be blessed by the LORD and find peace and joy in life.
  9. * Everyone who drinks of this water will be thirsty again; but whoever drinks of the water
  10. * that I will give him shall never thirst; John 4:13-14
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  12. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  13. * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  14. * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  15. * The above copyright, blessing, biblical verse, notice and this permission notice shall be included in all
  16. * copies or substantial portions of the Software.
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  18. * TO THE WARRANTIES OF MECHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  20. * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. * IN THE SOFTWARE.
  22. =================================================================================================================*/
  23. #ifndef GS_VG_H
  24. #define GS_VG_H
  25. /*
  26. USAGE: (IMPORTANT)
  27. =================================================================================================================
  28. Before including, define the gunslinger physics implementation like this:
  29. #define GS_VG_IMPL
  30. in EXACTLY ONE C or C++ file that includes this header, BEFORE the
  31. include, like this:
  32. #define GS_VG_IMPL
  33. #include "gs_vg.h"
  34. All other files should just #include "gs_vg.h" without the #define.
  35. MUST include "gs.h" and declare GS_IMPL BEFORE this file, since this file relies on that:
  36. #define GS_IMPL
  37. #include "gs.h"
  38. #define GS_VG_IMPL
  39. #include "gs_vg.h"
  40. +
  41. ================================================================================================================
  42. */
  43. //==== Interface ====//
  44. #define GS_VG_MITER_ANGLE_MIN 10.f
  45. #define GS_VG_ROUND_ANGLE_MIN 10.f
  46. #define GS_VG_SEGMENT_MAX 24
  47. enum
  48. {
  49. GS_VG_JOINT_MITER = 0x00,
  50. GS_VG_JOINT_BEVEL,
  51. GS_VG_JOINT_ROUND
  52. };
  53. enum
  54. {
  55. GS_VG_END_BUTT = 0x00,
  56. GS_VG_END_JOINT,
  57. GS_VG_END_SQUARE,
  58. GS_VG_END_ROUND
  59. };
  60. enum
  61. {
  62. GS_VG_WINDING_CW = 0x00,
  63. GS_VG_WINDING_CCW
  64. };
  65. enum
  66. {
  67. GS_VG_STROKE = 0x00,
  68. GS_VG_FILL
  69. };
  70. typedef struct
  71. {
  72. gs_vec2 position;
  73. gs_color_t color;
  74. float thickness;
  75. } gs_vg_point_t;
  76. GS_API_DECL gs_vg_point_t gs_vg_point_create(gs_vec2 position, gs_color_t color, float thickness);
  77. // Subpaths
  78. typedef struct
  79. {
  80. uint16_t start;
  81. uint16_t count;
  82. } gs_vg_path_t;
  83. typedef struct
  84. {
  85. int16_t mode; // Stroke/Fill
  86. int16_t joint; // Joint style
  87. int16_t end; // End style
  88. float thickness;
  89. gs_color_t color;
  90. uint16_t anti_alias;
  91. float aa_scale;
  92. } gs_vg_paint_t;
  93. typedef struct
  94. {
  95. gs_vg_point_t a;
  96. gs_vg_point_t b;
  97. } gs_vg_line_seg_t;
  98. typedef struct
  99. {
  100. b32 intersected;
  101. gs_vg_point_t point;
  102. } gs_vg_line_seg_intersection_t;
  103. typedef struct gs_vg_poly_seg_t
  104. {
  105. gs_vg_line_seg_t center;
  106. gs_vg_line_seg_t edge1;
  107. gs_vg_line_seg_t edge2;
  108. } gs_vg_poly_seg_t;
  109. typedef struct
  110. {
  111. gs_vec2 xform;
  112. gs_dyn_array(gs_vg_point_t) points;
  113. gs_dyn_array(gs_vg_path_t) paths;
  114. gs_dyn_array(gs_vg_poly_seg_t) segments;
  115. gs_vg_paint_t paint;
  116. } gs_vg_state_t;
  117. typedef struct
  118. {
  119. gs_immediate_draw_t gsi;
  120. gs_vg_state_t state;
  121. } gs_vg_ctx_t;
  122. GS_API_DECL gs_vg_ctx_t gs_vg_ctx_new();
  123. GS_API_DECL void gsvg_frame_begin(gs_vg_ctx_t* ctx, uint32_t ws, uint32_t wy);
  124. GS_API_DECL void gsvg_frame_end(gs_vg_ctx_t* ctx);
  125. GS_API_DECL void gsvg_path_begin(gs_vg_ctx_t* ctx);
  126. GS_API_DECL void gsvg_path_stroke(gs_vg_ctx_t* ctx);
  127. GS_API_DECL void gsvg_path_moveto(gs_vg_ctx_t* ctx, float x, float y);
  128. GS_API_DECL void gsvg_path_lineto(gs_vg_ctx_t* ctx, float x, float y);
  129. GS_API_DECL void gsvg_path_cbezierto(gs_vg_ctx_t* ctx, float x0, float y0,
  130. float x1, float y1, float x2, float y2);
  131. GS_API_DECL void gsvg_path_qbezierto(gs_vg_ctx_t* ctx, float x0, float y0, float x1, float y1);
  132. GS_API_DECL void gsvg_path_arcto(gs_vg_ctx_t* ctx, float x0, float y0, float x1, float y1, float radius);
  133. GS_API_DECL void gsvg_path_arc(gs_vg_ctx_t* ctx, float cx, float cy, float radius, float angle_start, float angle_end);
  134. GS_API_DECL void gsvg_paint(gs_vg_ctx_t* ctx, gs_vg_paint_t paint);
  135. GS_API_DECL void gsvg_render(gs_vg_ctx_t* ctx, gs_command_buffer_t* cb, uint32_t vw, uint32_t vh);
  136. GS_API_DECL void gsvg_renderpass_submit(gs_vg_ctx_t* ctx, gs_command_buffer_t* cb, gs_vec2 fbs, gs_color_t c);
  137. //==== Implementation ====//
  138. #ifdef GS_VG_IMPL
  139. // Forward decls
  140. GS_API_DECL void _gsvg_path_stroke_impl(gs_vg_ctx_t* ctx, gs_vg_path_t* path);
  141. GS_API_DECL void _gsvg_path_fill_impl(gs_vg_ctx_t* ctx);
  142. // Utils
  143. static bool gs_vg_point_equals(gs_vg_point_t* p0, gs_vg_point_t* p1)
  144. {
  145. return gs_vec2_equals(p0->position, p1->position);
  146. }
  147. static void gsvg_add_point(gs_vg_ctx_t* ctx, gs_vg_point_t point)
  148. {
  149. gs_vg_state_t* state = &ctx->state;
  150. gs_vg_path_t* path = &state->paths[gs_dyn_array_size(state->paths) - 1];
  151. if (gs_dyn_array_empty(state->points))
  152. {
  153. gs_dyn_array_push(state->points, point);
  154. path->count++;
  155. }
  156. // Determine whether or not previous point equals this one
  157. else
  158. {
  159. gs_vg_point_t* p = &((state->points)[gs_dyn_array_size(state->points) - 1]);
  160. if (!gs_vg_point_equals(p, &point))
  161. {
  162. gs_dyn_array_push(state->points, point);
  163. path->count++;
  164. }
  165. }
  166. }
  167. GS_API_DECL gs_vg_ctx_t gs_vg_ctx_new()
  168. {
  169. gs_vg_ctx_t ctx = gs_default_val();
  170. ctx.gsi = gs_immediate_draw_new();
  171. return ctx;
  172. }
  173. GS_API_DECL void gsvg_frame_begin(gs_vg_ctx_t* ctx, uint32_t vw, uint32_t vh)
  174. {
  175. gs_dyn_array_clear(ctx->state.points);
  176. gs_dyn_array_clear(ctx->state.paths);
  177. gsi_camera2D(&ctx->gsi, vw, vh);
  178. gsi_blend_enabled(&ctx->gsi, true);
  179. gsi_begin(&ctx->gsi, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
  180. gsi_tc2fv(&ctx->gsi, gs_v2s(0.f));
  181. ctx->state.paint.color = GS_COLOR_WHITE;
  182. ctx->state.paint.thickness = 1.f;
  183. ctx->state.paint.end = GS_VG_END_BUTT;
  184. ctx->state.paint.joint = GS_VG_JOINT_MITER;
  185. ctx->state.paint.anti_alias = true;
  186. ctx->state.paint.aa_scale = 0.5f;
  187. }
  188. GS_API_DECL void gsvg_frame_end(gs_vg_ctx_t* ctx)
  189. {
  190. // Not sure what to do here...
  191. }
  192. GS_API_DECL void gsvg_paint(gs_vg_ctx_t* ctx, gs_vg_paint_t paint)
  193. {
  194. ctx->state.paint = paint;
  195. }
  196. static bool _gsvg_path_end_impl(gs_vg_ctx_t* ctx)
  197. {
  198. gs_vg_state_t* state = &ctx->state;
  199. gs_vg_path_t* path = &state->paths[gs_dyn_array_size(state->paths) - 1];
  200. return path->count;
  201. }
  202. GS_API_DECL void gsvg_path_begin(gs_vg_ctx_t* ctx)
  203. {
  204. gs_vg_state_t* state = &ctx->state;
  205. if (gs_dyn_array_empty(ctx->state.paths))
  206. {
  207. gs_vg_path_t path = gs_default_val();
  208. gs_dyn_array_push(state->paths, path);
  209. }
  210. else
  211. {
  212. // End previous path, if points were written
  213. if ( _gsvg_path_end_impl(ctx))
  214. {
  215. gs_vg_path_t path = gs_default_val();
  216. path.start = gs_dyn_array_size(state->points);
  217. gs_dyn_array_push(state->paths, path);
  218. }
  219. }
  220. }
  221. GS_API_DECL void gsvg_path_fill(gs_vg_ctx_t* ctx)
  222. {
  223. gs_vg_state_t* state = &ctx->state;
  224. // End previous path
  225. _gsvg_path_end_impl(ctx);
  226. _gsvg_path_fill_impl(ctx);
  227. // Just use naive ear-clipping for now, don't respect holes
  228. gs_dyn_array_clear(state->points);
  229. gs_dyn_array_clear(state->paths);
  230. gs_dyn_array_clear(state->segments);
  231. }
  232. GS_API_DECL void gsvg_path_stroke(gs_vg_ctx_t* ctx)
  233. {
  234. // End previous path
  235. // Iterate through all sub-paths and stroke
  236. gs_vg_state_t* state = &ctx->state;
  237. // End previous path
  238. _gsvg_path_end_impl(ctx);
  239. // For each subpath, stroke
  240. for (uint32_t i = 0; i < gs_dyn_array_size(state->paths); ++i)
  241. {
  242. gs_vg_path_t* path = &state->paths[i];
  243. _gsvg_path_stroke_impl(ctx, path);
  244. gs_dyn_array_clear(state->segments);
  245. }
  246. // Clear previous paths, segments, and points
  247. gs_dyn_array_clear(state->points);
  248. gs_dyn_array_clear(state->paths);
  249. gs_dyn_array_clear(state->segments);
  250. }
  251. GS_API_DECL void gsvg_path_close(gs_vg_ctx_t* ctx)
  252. {
  253. ctx->state.paint.end = GS_VG_END_JOINT;
  254. }
  255. GS_API_DECL void gsvg_path_moveto(gs_vg_ctx_t* ctx, float x, float y)
  256. {
  257. gsvg_path_begin(ctx);
  258. ctx->state.xform = gs_v2(x, y);
  259. }
  260. GS_API_DECL void gsvg_path_lineto(gs_vg_ctx_t* ctx, float x, float y)
  261. {
  262. gs_vg_state_t* state = &ctx->state;
  263. gs_vg_paint_t* paint = &state->paint;
  264. gs_vg_point_t p0 = gs_vg_point_create(state->xform, paint->color, paint->thickness);
  265. gs_vg_point_t p1 = gs_vg_point_create(gs_v2(x, y), paint->color, paint->thickness);
  266. gsvg_add_point(ctx, p0);
  267. gsvg_add_point(ctx, p1);
  268. state->xform = p1.position;
  269. }
  270. GS_API_DECL void gsvg_path_qbezierto(gs_vg_ctx_t* ctx, float x1, float y1, float x2, float y2)
  271. {
  272. gs_vg_state_t* state = &ctx->state;
  273. gs_vg_paint_t* paint = &state->paint;
  274. float x0 = state->xform.x;
  275. float y0 = state->xform.y;
  276. gs_vg_point_t p = gs_default_val();
  277. uint32_t num_segs = GS_VG_SEGMENT_MAX;
  278. float step = 1.0f / (float)num_segs;
  279. for (float s = 0.f; s < 1.f; s += step)
  280. {
  281. float xa = gs_interp_linear(x0, x1, s);
  282. float ya = gs_interp_linear(y0, y1, s);
  283. float xb = gs_interp_linear(x1, x2, s);
  284. float yb = gs_interp_linear(y1, y2, s);
  285. float x = gs_interp_linear(xa, xb, s);
  286. float y = gs_interp_linear(ya, yb, s);
  287. p = gs_vg_point_create(gs_v2(x, y), paint->color, paint->thickness);
  288. gsvg_add_point(ctx, p);
  289. }
  290. state->xform = p.position;
  291. }
  292. GS_API_DECL void gsvg_path_cbezierto(gs_vg_ctx_t* ctx, float x1, float y1,
  293. float x2, float y2, float x3, float y3)
  294. {
  295. gs_vg_state_t* state = &ctx->state;
  296. gs_vg_paint_t* paint = &state->paint;
  297. float x0 = state->xform.x;
  298. float y0 = state->xform.y;
  299. gs_vg_point_t p = gs_default_val();
  300. uint32_t num_segs = GS_VG_SEGMENT_MAX;
  301. float step = 1.0f / (float)num_segs;
  302. for (float s = 0.f; s < 1.f; s += step)
  303. {
  304. float xa = gs_interp_linear(x0, x1, s);
  305. float ya = gs_interp_linear(y0, y1, s);
  306. float xb = gs_interp_linear(x1, x2, s);
  307. float yb = gs_interp_linear(y1, y2, s);
  308. float xc = gs_interp_linear(x2, x3, s);
  309. float yc = gs_interp_linear(y2, y3, s);
  310. float xm = gs_interp_linear(xa, xb, s);
  311. float ym = gs_interp_linear(ya, yb, s);
  312. float xn = gs_interp_linear(xb, xc, s);
  313. float yn = gs_interp_linear(yb, yc, s);
  314. float x = gs_interp_linear(xm, xn, s);
  315. float y = gs_interp_linear(ym, yn, s);
  316. p = gs_vg_point_create(gs_v2(x, y), paint->color, paint->thickness);
  317. gsvg_add_point(ctx, p);
  318. }
  319. state->xform = p.position;
  320. }
  321. void _gsvg_path_arc_impl(gs_vg_ctx_t* ctx, float cx, float cy, float r, float sa, float ea)
  322. {
  323. gs_vg_state_t* state = &ctx->state;
  324. gs_vg_paint_t* paint = &state->paint;
  325. if (sa > ea) {
  326. f32 tmp = sa;
  327. sa = ea;
  328. ea = tmp;
  329. }
  330. f32 diff = ea - sa;
  331. if (fabsf(diff) <= 0.001f) {
  332. return;
  333. }
  334. gs_vec2 origin = gs_v2(cx, cy);
  335. gs_vg_point_t p = gs_default_val();
  336. uint32_t num_segments = GS_VG_SEGMENT_MAX;
  337. f32 step = num_segments < 5 ? diff / 5.f : diff / (f32)num_segments;
  338. // Need CW vs. CCW here...
  339. for (f32 i = sa; i <= ea; i += step)
  340. {
  341. f32 a = gs_deg2rad(i);
  342. gs_vec2 pos = gs_vec2_add(origin, gs_vec2_scale(gs_v2(cos(a), sin(a)), r));
  343. p = gs_vg_point_create(pos, paint->color, paint->thickness);
  344. gsvg_add_point(ctx, p);
  345. }
  346. // Push last angle on as well
  347. {
  348. f32 a = gs_deg2rad(ea);
  349. gs_vec2 pos = gs_vec2_add(origin, gs_vec2_scale(gs_v2(cos(a), sin(a)), r));
  350. p = gs_vg_point_create(pos, paint->color, paint->thickness);
  351. gsvg_add_point(ctx, p);
  352. }
  353. }
  354. GS_API_DECL void gsvg_path_arcto(gs_vg_ctx_t* ctx, float x1, float y1, float x2, float y2, float radius)
  355. {
  356. gs_vg_state_t* state = &ctx->state;
  357. gs_vg_paint_t* paint = &state->paint;
  358. float x0 = state->xform.x, y0 = state->xform.y;
  359. gs_vec2 d0 = gs_v2s(0.f);
  360. gs_vec2 d1 = gs_v2s(0.f);
  361. gs_vec2 c = gs_v2s(0.f);
  362. float a = 0.f, d = 0.f, a0 = 0.f, a1 = 0.f;
  363. int16_t dir = GS_VG_WINDING_CW;
  364. gs_vg_point_t p = gs_vg_point_create(state->xform, paint->color, paint->thickness);
  365. gsvg_add_point(ctx, p);
  366. // CW vs. CCW depending on points
  367. d0 = gs_vec2_norm(gs_vec2_sub(gs_v2(x0, y0), gs_v2(x1, y1)));
  368. d1 = gs_vec2_norm(gs_vec2_sub(gs_v2(x2, y2), gs_v2(x1, y1)));
  369. a = acosf(gs_vec2_dot(d0, d1));
  370. d = radius / tanf(a * 0.5f);
  371. if (d > 10000.f)
  372. {
  373. gsvg_path_lineto(ctx, x1, y1);
  374. return;
  375. }
  376. if (gs_vec2_cross(d0, d1) > 0.f)
  377. {
  378. c.x = x1 + d0.x * d + d0.y * radius;
  379. c.y = y1 + d0.y * d - d0.x * radius;
  380. a0 = atan2f(d0.x, -d0.y);
  381. a1 = atan2f(-d1.x, d1.y);
  382. dir = GS_VG_WINDING_CW;
  383. }
  384. else
  385. {
  386. c.x = x1 + d0.x * d - d0.y * radius;
  387. c.y = y1 + d0.y * d + d0.x * radius;
  388. a0 = atan2f(-d0.x, d0.y);
  389. a1 = atan2f(d1.x, -d1.y);
  390. dir = GS_VG_WINDING_CCW;
  391. }
  392. _gsvg_path_arc_impl(ctx, c.x, c.y, radius, gs_rad2deg(a0), gs_rad2deg(a1));
  393. const gs_vg_point_t* last = &state->points[gs_dyn_array_size(state->points) - 1];
  394. state->xform = last->position;
  395. }
  396. GS_API_DECL void gsvg_path_arc(gs_vg_ctx_t* ctx, float cx, float cy, float r, float sa, float ea)
  397. {
  398. gsvg_path_moveto(ctx, cx, cy);
  399. _gsvg_path_arc_impl(ctx, cx, cy, r, sa, ea);
  400. const gs_vg_point_t* last = &ctx->state.points[gs_dyn_array_size(ctx->state.points) - 1];
  401. gsvg_path_moveto(ctx, last->position.x, last->position.y);
  402. }
  403. GS_API_DECL void gsvg_render(gs_vg_ctx_t* ctx, gs_command_buffer_t* cb, uint32_t vw, uint32_t vh)
  404. {
  405. gsi_draw(&ctx->gsi, cb);
  406. }
  407. GS_API_DECL void gsvg_renderpass_submit(gs_vg_ctx_t* ctx, gs_command_buffer_t* cb, gs_vec2 fbs, gs_color_t c)
  408. {
  409. gs_graphics_clear_action_t action = gs_default_val();
  410. action.color[0] = (float)c.r / 255.f;
  411. action.color[1] = (float)c.g / 255.f;
  412. action.color[2] = (float)c.b / 255.f;
  413. action.color[3] = (float)c.a / 255.f;
  414. gs_graphics_clear_desc_t clear = gs_default_val();
  415. clear.actions = &action;
  416. gs_graphics_renderpass_begin(cb, (gs_handle(gs_graphics_renderpass_t)){0});
  417. {
  418. gs_graphics_clear(cb, &clear);
  419. gs_graphics_set_viewport(cb, 0, 0, (uint32_t)fbs.x,(uint32_t)fbs.y);
  420. gsvg_render(ctx, cb, (uint32_t)fbs.x, (uint32_t)fbs.y);
  421. }
  422. gs_graphics_renderpass_end(cb);
  423. }
  424. #define miter_min_angle gs_deg2rad(GS_VG_MITER_ANGLE_MIN)
  425. #define round_min_angle gs_deg2rad(GS_VG_ROUND_ANGLE_MIN)
  426. gs_vg_point_t gs_vg_point_create(gs_vec2 position, gs_color_t color, float thickness)
  427. {
  428. gs_vg_point_t pt = gs_default_val();
  429. pt.position = position;
  430. pt.color = color;
  431. pt.thickness = thickness;
  432. return pt;
  433. }
  434. gs_vg_point_t gs_vg_point_add(gs_vg_point_t p, gs_vec2 os)
  435. {
  436. gs_vg_point_t pp = p;
  437. pp.position = gs_vec2_add(pp.position, os);
  438. return pp;
  439. }
  440. gs_vg_point_t gs_vg_point_sub(gs_vg_point_t p, gs_vec2 os)
  441. {
  442. return gs_vg_point_add(p, gs_vec2_scale(os, -1.f));
  443. }
  444. gs_vg_line_seg_t
  445. gs_vg_line_seg_add(gs_vg_line_seg_t l, gs_vec2 os)
  446. {
  447. gs_vg_line_seg_t ls = gs_default_val();
  448. ls.a = gs_vg_point_add(l.a, os);
  449. ls.b = gs_vg_point_add(l.b, os);
  450. return ls;
  451. }
  452. gs_vg_line_seg_t
  453. gs_vg_line_seg_sub(gs_vg_line_seg_t l, gs_vec2 os)
  454. {
  455. gs_vg_line_seg_t ls = gs_default_val();
  456. ls.a = gs_vg_point_sub(l.a, os);
  457. ls.b = gs_vg_point_sub(l.b, os);
  458. return ls;
  459. }
  460. gs_vec2 gs_vg_line_seg_dir(gs_vg_line_seg_t l, b32 normalized)
  461. {
  462. gs_vec2 vec = gs_vec2_sub(l.b.position, l.a.position);
  463. return normalized ? gs_vec2_norm(vec) : vec;
  464. }
  465. // Not sure about this one...
  466. gs_vec2 gs_vg_line_seg_normal(gs_vg_line_seg_t l)
  467. {
  468. gs_vec2 dir = gs_vg_line_seg_dir(l, true);
  469. // return the direction vector
  470. // rotated by 90 degrees counter-clockwise
  471. return (gs_vec2){-dir.y, dir.x};
  472. }
  473. gs_vg_line_seg_intersection_t null_intersection()
  474. {
  475. gs_vg_line_seg_intersection_t ls = gs_default_val();
  476. return ls;
  477. }
  478. gs_vg_line_seg_intersection_t gs_vg_line_seg_intersection_create(gs_vg_point_t pt, b32 intersected)
  479. {
  480. gs_vg_line_seg_intersection_t ls = gs_default_val();
  481. ls.intersected = intersected;
  482. ls.point = pt;
  483. return ls;
  484. }
  485. gs_vg_line_seg_intersection_t
  486. gs_vg_line_seg_intersection(gs_vg_line_seg_t a, gs_vg_line_seg_t b, b32 infiniteLines)
  487. {
  488. // calculate un-normalized direction vectors
  489. gs_vec2 r = gs_vg_line_seg_dir(a, false);
  490. gs_vec2 s = gs_vg_line_seg_dir(b, false);
  491. gs_vec2 origin_dist = gs_vec2_sub(b.a.position, a.a.position);
  492. f32 u_numerator = gs_vec2_cross(origin_dist, r);
  493. f32 denominator = gs_vec2_cross(r, s);
  494. // The lines are parallel
  495. if (fabsf(denominator) < 0.0001f) {
  496. return null_intersection();
  497. }
  498. // solve the intersection positions
  499. f32 u = u_numerator / denominator;
  500. f32 t = gs_vec2_cross(origin_dist, s) / denominator;
  501. if (!infiniteLines && (t < 0 || t > 1 || u < 0 || u > 1)) {
  502. // the intersection lies outside of the line segments
  503. return null_intersection();
  504. }
  505. // calculate the intersection point
  506. // a.a + r * t;
  507. return gs_vg_line_seg_intersection_create(gs_vg_point_add(a.a, gs_vec2_scale(r, t)), true);
  508. }
  509. gs_vg_poly_seg_t gs_vg_poly_seg_create(gs_vg_line_seg_t center)
  510. {
  511. gs_vg_poly_seg_t p = {0};
  512. p.center = center;
  513. // Need to create these manually
  514. gs_vec2 n = gs_vg_line_seg_normal(center);
  515. // Edge1.a will be a line segment from center + norm * a;
  516. p.edge1.a = gs_vg_point_add(center.a, gs_vec2_scale(n, center.a.thickness));
  517. p.edge1.b = gs_vg_point_add(center.b, gs_vec2_scale(n, center.b.thickness));
  518. // Edge1.b will be a line segment from center - norm * a;
  519. p.edge2.a = gs_vg_point_sub(center.a, gs_vec2_scale(n, center.a.thickness));
  520. p.edge2.b = gs_vg_point_sub(center.b, gs_vec2_scale(n, center.b.thickness));
  521. return p;
  522. }
  523. void _gsvg_path_stroke_joint_impl(gs_vg_ctx_t* ctx, gs_vg_poly_seg_t seg1, gs_vg_poly_seg_t seg2,
  524. int16_t joint_style, gs_vg_point_t* end1, gs_vg_point_t* end2,
  525. gs_vg_point_t* next_start1, gs_vg_point_t* next_start2,
  526. b32 allow_overlap);
  527. GS_API_DECL void _gsvg_path_stroke_triangle_fan_impl(gs_vg_ctx_t* ctx, gs_vg_point_t conenct_to,
  528. gs_vg_point_t origin, gs_vg_point_t start, gs_vg_point_t end, bool clockwise);
  529. GS_API_DECL void _gsvg_path_stroke_impl(gs_vg_ctx_t* ctx, gs_vg_path_t* path)
  530. {
  531. gs_vg_state_t* state = &ctx->state;
  532. gs_vg_paint_t* paint = &state->paint;
  533. int16_t end_cap_style = paint->end;
  534. int16_t joint_style = paint->joint;
  535. bool allow_overlap = false;
  536. for (u32 i = path->start; i + 1 < path->start + path->count; ++i)
  537. {
  538. gs_vg_point_t point1 = state->points[i];
  539. gs_vg_point_t point2 = state->points[i + 1];
  540. // to avoid division-by-zero errors,
  541. // only create a line segment for non-identical points
  542. if (!gs_vec2_equal(point1.position, point2.position))
  543. {
  544. gs_vg_line_seg_t center = gs_default_val();
  545. center.a = point1;
  546. center.b = point2;
  547. gs_vg_poly_seg_t ps = gs_vg_poly_seg_create(center);
  548. gs_dyn_array_push(state->segments, ps);
  549. }
  550. }
  551. if (end_cap_style == GS_VG_END_JOINT) {
  552. // create a connecting segment from the last to the first point
  553. gs_vg_point_t point1 = state->points[path->start + path->count - 1];
  554. gs_vg_point_t point2 = state->points[path->start];
  555. // to avoid division-by-zero errors,
  556. // only create a line segment for non-identical points
  557. if (!gs_vec2_equal(point1.position, point2.position))
  558. {
  559. gs_vg_line_seg_t center = gs_default_val();
  560. center.a = point1;
  561. center.b = point2;
  562. gs_vg_poly_seg_t ps = gs_vg_poly_seg_create(center);
  563. gs_dyn_array_push(state->segments, ps);
  564. }
  565. }
  566. if (gs_dyn_array_empty(state->segments)) {
  567. // handle the case of insufficient input points
  568. return;
  569. }
  570. gs_vg_point_t next_start1 = {0};
  571. gs_vg_point_t next_start2 = {0};
  572. gs_vg_point_t start1 = {0};
  573. gs_vg_point_t start2 = {0};
  574. gs_vg_point_t end1 = {0};
  575. gs_vg_point_t end2 = {0};
  576. // calculate the path's global start and end points
  577. gs_vg_poly_seg_t first_segment = state->segments[0];
  578. gs_vg_poly_seg_t last_segment = state->segments[gs_dyn_array_size(state->segments) - 1];
  579. gs_vg_point_t path_start1 = first_segment.edge1.a;
  580. gs_vg_point_t path_start2 = first_segment.edge2.a;
  581. gs_vg_point_t path_end1 = last_segment.edge1.b;
  582. gs_vg_point_t path_end2 = last_segment.edge2.b;
  583. // handle different end cap styles
  584. if (end_cap_style == GS_VG_END_SQUARE)
  585. {
  586. path_start1 = gs_vg_point_sub(path_start1,
  587. gs_vec2_scale(gs_vg_line_seg_dir(first_segment.edge1, true), first_segment.edge1.a.thickness));
  588. path_start2 = gs_vg_point_sub(path_start2,
  589. gs_vec2_scale(gs_vg_line_seg_dir(first_segment.edge2, true), first_segment.edge1.a.thickness));
  590. path_end1 = gs_vg_point_add(path_end1,
  591. gs_vec2_scale(gs_vg_line_seg_dir(last_segment.edge1, true), last_segment.edge1.b.thickness));
  592. path_end2 = gs_vg_point_add(path_end2,
  593. gs_vec2_scale(gs_vg_line_seg_dir(last_segment.edge2, true), last_segment.edge1.b.thickness));
  594. }
  595. else if (end_cap_style == GS_VG_END_ROUND)
  596. {
  597. // draw half circle end caps
  598. _gsvg_path_stroke_triangle_fan_impl(ctx, first_segment.center.a, first_segment.center.a,
  599. first_segment.edge1.a, first_segment.edge2.a, false);
  600. _gsvg_path_stroke_triangle_fan_impl(ctx, last_segment.center.b, last_segment.center.b,
  601. last_segment.edge1.b, last_segment.edge2.b, true);
  602. }
  603. else if (end_cap_style == GS_VG_END_JOINT)
  604. {
  605. // join the last (connecting) segment and the first segment
  606. _gsvg_path_stroke_joint_impl(ctx, last_segment, first_segment, joint_style,
  607. &path_end1, &path_end2, &path_start1, &path_start2, false);
  608. }
  609. // generate mesh data for path segments
  610. for (u32 i = 0; i < gs_dyn_array_size(state->segments); ++i) {
  611. gs_vg_poly_seg_t segment = state->segments[i];
  612. // calculate start
  613. if (i == 0)
  614. {
  615. // this is the first segment
  616. start1 = path_start1;
  617. start2 = path_start2;
  618. }
  619. if (i + 1 == gs_dyn_array_size(state->segments))
  620. {
  621. // this is the last segment
  622. end1 = path_end1;
  623. end2 = path_end2;
  624. }
  625. else
  626. {
  627. _gsvg_path_stroke_joint_impl(ctx, segment, state->segments[i + 1], joint_style,
  628. &end1, &end2, &next_start1, &next_start2, allow_overlap);
  629. }
  630. float anti_alias_scl = paint->aa_scale;
  631. if (paint->anti_alias)
  632. {
  633. f32 s_thick = gs_max(start1.thickness, start2.thickness);
  634. f32 e_thick = gs_max(end1.thickness, end2.thickness);
  635. // Push back verts for anti-aliasing as well...somehow
  636. gs_vec2 sn = gs_vec2_norm(gs_vec2_sub(start2.position, start1.position));
  637. gs_vec2 en = gs_vec2_norm(gs_vec2_sub(end2.position, end1.position));
  638. const f32 sl = (gs_vec2_len(gs_vec2_sub(start2.position, start1.position)) / (s_thick)) * anti_alias_scl;
  639. const f32 el = (gs_vec2_len(gs_vec2_sub(end2.position, end1.position)) / (e_thick)) * anti_alias_scl;
  640. gs_color_t s1_col = gs_color_alpha(start1.color, 0);
  641. gs_color_t s2_col = gs_color_alpha(start2.color, 0);
  642. gs_color_t e1_col = gs_color_alpha(end1.color, 0);
  643. gs_color_t e2_col = gs_color_alpha(end2.color, 0);
  644. gs_vg_point_t s1 = gs_vg_point_sub(start1, gs_vec2_scale(sn, sl));
  645. gs_vg_point_t s2 = gs_vg_point_add(start2, gs_vec2_scale(sn, sl));
  646. gs_vg_point_t e1 = gs_vg_point_sub(end1, gs_vec2_scale(en, el));
  647. gs_vg_point_t e2 = gs_vg_point_add(end2, gs_vec2_scale(en, el));
  648. gsi_c4ubv(&ctx->gsi, start1.color); gsi_v2fv(&ctx->gsi, start1.position);
  649. gsi_c4ubv(&ctx->gsi, s1_col); gsi_v2fv(&ctx->gsi, s1.position);
  650. gsi_c4ubv(&ctx->gsi, e1_col); gsi_v2fv(&ctx->gsi, e1.position);
  651. gsi_c4ubv(&ctx->gsi, e1_col); gsi_v2fv(&ctx->gsi, e1.position);
  652. gsi_c4ubv(&ctx->gsi, end1.color); gsi_v2fv(&ctx->gsi, end1.position);
  653. gsi_c4ubv(&ctx->gsi, start1.color); gsi_v2fv(&ctx->gsi, start1.position);
  654. gsi_c4ubv(&ctx->gsi, s2_col); gsi_v2fv(&ctx->gsi, s2.position);
  655. gsi_c4ubv(&ctx->gsi, start2.color); gsi_v2fv(&ctx->gsi, start2.position);
  656. gsi_c4ubv(&ctx->gsi, e2_col); gsi_v2fv(&ctx->gsi, e2.position);
  657. gsi_c4ubv(&ctx->gsi, e2_col); gsi_v2fv(&ctx->gsi, e2.position);
  658. gsi_c4ubv(&ctx->gsi, end2.color); gsi_v2fv(&ctx->gsi, end2.position);
  659. gsi_c4ubv(&ctx->gsi, start2.color); gsi_v2fv(&ctx->gsi, start2.position);
  660. // If we're at beginning and not end_cap_joint, then we need to anti-alias edge
  661. if (i == 0 && (end_cap_style == GS_VG_END_SQUARE
  662. || end_cap_style == GS_VG_END_BUTT) )
  663. {
  664. gs_vg_point_t s1s = gs_vg_point_sub(start1, gs_vec2_scale(sn, sl * 0.5f));
  665. gs_vg_point_t s2s = gs_vg_point_add(start2, gs_vec2_scale(sn, sl * 0.5f));
  666. gs_vec2 snc = gs_v2(-sn.y, sn.x);
  667. gs_vg_point_t s1c = gs_vg_point_add(s1s, gs_vec2_scale(snc, sl));
  668. gs_vg_point_t s2c = gs_vg_point_add(s2s, gs_vec2_scale(snc, sl));
  669. gsi_c4ubv(&ctx->gsi, s1_col); gsi_v2fv(&ctx->gsi, s1c.position);
  670. gsi_c4ubv(&ctx->gsi, s1s.color); gsi_v2fv(&ctx->gsi, s1s.position);
  671. gsi_c4ubv(&ctx->gsi, s2_col); gsi_v2fv(&ctx->gsi, s2c.position);
  672. gsi_c4ubv(&ctx->gsi, s2_col); gsi_v2fv(&ctx->gsi, s2c.position);
  673. gsi_c4ubv(&ctx->gsi, s2s.color); gsi_v2fv(&ctx->gsi, s2s.position);
  674. gsi_c4ubv(&ctx->gsi, s1s.color); gsi_v2fv(&ctx->gsi, s1s.position);
  675. }
  676. // If we're at end and not end_cap_joint, then we need to anti-alias edge
  677. if ((i + 1) == gs_dyn_array_size(state->segments)
  678. && (end_cap_style == GS_VG_END_SQUARE
  679. || end_cap_style == GS_VG_END_BUTT) )
  680. {
  681. gs_vg_point_t e1s = gs_vg_point_sub(end1, gs_vec2_scale(en, el * 0.5f));
  682. gs_vg_point_t e2s = gs_vg_point_add(end2, gs_vec2_scale(en, el * 0.5f));
  683. gs_vec2 enc = gs_v2(-en.y, en.x);
  684. gs_vg_point_t e1c = gs_vg_point_add(e1s, gs_vec2_scale(enc, el));
  685. gs_vg_point_t e2c = gs_vg_point_add(e2s, gs_vec2_scale(enc, el));
  686. gsi_c4ubv(&ctx->gsi, e1_col); gsi_v2fv(&ctx->gsi, e1c.position);
  687. gsi_c4ubv(&ctx->gsi, e1s.color); gsi_v2fv(&ctx->gsi, e1s.position);
  688. gsi_c4ubv(&ctx->gsi, e2_col); gsi_v2fv(&ctx->gsi, e2c.position);
  689. gsi_c4ubv(&ctx->gsi, e2_col); gsi_v2fv(&ctx->gsi, e2c.position);
  690. gsi_c4ubv(&ctx->gsi, e2s.color); gsi_v2fv(&ctx->gsi, e2s.position);
  691. gsi_c4ubv(&ctx->gsi, e1s.color); gsi_v2fv(&ctx->gsi, e1s.position);
  692. }
  693. }
  694. // Push back verts
  695. gsi_c4ubv(&ctx->gsi, start1.color); gsi_v2fv(&ctx->gsi, start1.position);
  696. gsi_c4ubv(&ctx->gsi, start2.color); gsi_v2fv(&ctx->gsi, start2.position);
  697. gsi_c4ubv(&ctx->gsi, end1.color); gsi_v2fv(&ctx->gsi, end1.position);
  698. gsi_c4ubv(&ctx->gsi, end1.color); gsi_v2fv(&ctx->gsi, end1.position);
  699. gsi_c4ubv(&ctx->gsi, start2.color); gsi_v2fv(&ctx->gsi, start2.position);
  700. gsi_c4ubv(&ctx->gsi, end2.color); gsi_v2fv(&ctx->gsi, end2.position);
  701. start1 = next_start1;
  702. start2 = next_start2;
  703. }
  704. }
  705. GS_API_DECL void _gsvg_path_stroke_triangle_fan_impl(gs_vg_ctx_t* ctx, gs_vg_point_t connect_to,
  706. gs_vg_point_t origin, gs_vg_point_t start, gs_vg_point_t end, bool clockwise)
  707. {
  708. gs_vg_paint_t* paint = &ctx->state.paint;
  709. gs_vg_point_t point1 = gs_vg_point_sub(start, origin.position);
  710. gs_vg_point_t point2 = gs_vg_point_sub(end, origin.position);
  711. // calculate the angle between the two points
  712. f32 angle1 = atan2(point1.position.y, point1.position.x);
  713. f32 angle2 = atan2(point2.position.y, point2.position.x);
  714. // ensure the outer angle is calculated
  715. if (clockwise)
  716. {
  717. if (angle2 > angle1) {
  718. angle2 = angle2 - 2.0 * GS_PI;
  719. }
  720. }
  721. else
  722. {
  723. if (angle1 > angle2) {
  724. angle1 = angle1 - 2.0 * GS_PI;
  725. }
  726. }
  727. f32 joint_angle = angle2 - angle1;
  728. // calculate the amount of triangles to use for the joint
  729. s32 num_triangles = (s32)gs_max(1, (float)floor(fabsf(joint_angle) / round_min_angle));
  730. // calculate the angle of each triangle
  731. f32 tri_angle = joint_angle / (f32)num_triangles;
  732. gs_vg_point_t start_point = start;
  733. gs_vg_point_t end_point = end;
  734. for (s32 t = 0; t < num_triangles; t++)
  735. {
  736. if (t + 1 == num_triangles)
  737. {
  738. // it's the last triangle - ensure it perfectly
  739. // connects to the next line
  740. end_point = end;
  741. } else
  742. {
  743. f32 rot = (t + 1) * tri_angle;
  744. // rotate the original point around the origin
  745. end_point.position.x = cos(rot) * point1.position.x - sin(rot) * point1.position.y;
  746. end_point.position.y = sin(rot) * point1.position.x + cos(rot) * point1.position.y;
  747. // re-add the rotation origin to the target point
  748. end_point = gs_vg_point_add(end_point, origin.position);
  749. }
  750. // Emit verts directly into gsi
  751. gsi_c4ubv(&ctx->gsi, start_point.color); gsi_v2fv(&ctx->gsi, start_point.position);
  752. gsi_c4ubv(&ctx->gsi, end_point.color); gsi_v2fv(&ctx->gsi, end_point.position);
  753. gsi_c4ubv(&ctx->gsi, connect_to.color); gsi_v2fv(&ctx->gsi, connect_to.position);
  754. float anti_alias_scl = paint->aa_scale;
  755. if (paint->anti_alias)
  756. {
  757. gs_vec2 ns = gs_vec2_norm(gs_vec2_sub(start_point.position, connect_to.position));
  758. gs_vec2 ne = gs_vec2_norm(gs_vec2_sub(end_point.position, connect_to.position));
  759. gs_color_t s_col = gs_color_alpha(start_point.color, 0);
  760. gs_color_t e_col = gs_color_alpha(end_point.color, 0);
  761. const f32 sl = (gs_vec2_len(gs_vec2_sub(start_point.position, connect_to.position)) / (start_point.thickness)) * anti_alias_scl;
  762. const f32 el = (gs_vec2_len(gs_vec2_sub(end_point.position, connect_to.position)) / (end_point.thickness)) * anti_alias_scl;
  763. gs_vg_point_t s = gs_vg_point_add(start_point, gs_vec2_scale(ns, sl));
  764. gs_vg_point_t e = gs_vg_point_add(end_point, gs_vec2_scale(ne, el));
  765. gsi_c4ubv(&ctx->gsi, s_col); gsi_v2fv(&ctx->gsi, s.position);
  766. gsi_c4ubv(&ctx->gsi, start_point.color); gsi_v2fv(&ctx->gsi, start_point.position);
  767. gsi_c4ubv(&ctx->gsi, e_col); gsi_v2fv(&ctx->gsi, e.position);
  768. gsi_c4ubv(&ctx->gsi, e_col); gsi_v2fv(&ctx->gsi, e.position);
  769. gsi_c4ubv(&ctx->gsi, end_point.color); gsi_v2fv(&ctx->gsi, end_point.position);
  770. gsi_c4ubv(&ctx->gsi, start_point.color); gsi_v2fv(&ctx->gsi, start_point.position);
  771. }
  772. start_point = end_point;
  773. }
  774. }
  775. void _gsvg_path_stroke_joint_impl(gs_vg_ctx_t* ctx,
  776. gs_vg_poly_seg_t seg1, gs_vg_poly_seg_t seg2,
  777. int16_t joint_style, gs_vg_point_t* end1, gs_vg_point_t* end2,
  778. gs_vg_point_t* next_start1, gs_vg_point_t* next_start2, b32 allow_overlap)
  779. {
  780. gs_vg_paint_t* paint = &ctx->state.paint;
  781. // calculate the angle between the two line segments
  782. gs_vec2 dir1 = gs_vg_line_seg_dir(seg1.center, true);
  783. gs_vec2 dir2 = gs_vg_line_seg_dir(seg2.center, true);
  784. f32 angle = gs_vec2_angle(dir1, dir2);
  785. // wrap the angle around the 180° mark if it exceeds 90°
  786. // for minimum angle detection
  787. f32 wrapped_angle = angle;
  788. if (wrapped_angle > GS_PI / 2) {
  789. wrapped_angle = GS_PI - wrapped_angle;
  790. }
  791. if (joint_style == GS_VG_JOINT_MITER && wrapped_angle < miter_min_angle) {
  792. joint_style = GS_VG_JOINT_BEVEL;
  793. }
  794. if (joint_style == GS_VG_JOINT_MITER) {
  795. // calculate each edge's intersection point
  796. // with the next segment's central line
  797. gs_vg_line_seg_intersection_t sec1 = gs_vg_line_seg_intersection(seg1.edge1, seg2.edge1, true);
  798. gs_vg_line_seg_intersection_t sec2 = gs_vg_line_seg_intersection(seg1.edge2, seg2.edge2, true);
  799. *end1 = sec1.intersected ? sec1.point : seg1.edge1.b;
  800. *end2 = sec2.intersected ? sec2.point : seg1.edge2.b;
  801. *next_start1 = *end1;
  802. *next_start2 = *end2;
  803. }
  804. else
  805. {
  806. // joint style is either BEVEL or ROUND
  807. // find out which are the inner edges for this joint
  808. f32 x1 = dir1.x;
  809. f32 x2 = dir2.x;
  810. f32 y1 = dir1.y;
  811. f32 y2 = dir2.y;
  812. b32 clockwise = (x1 * y2 - x2 * y1) < 0;
  813. gs_vg_line_seg_t *inner1, *inner2, *outer1, *outer2;
  814. // as the normal vector is rotated counter-clockwise,
  815. // the first edge lies to the left
  816. // from the central line's perspective,
  817. // and the second one to the right.
  818. if (clockwise) {
  819. outer1 = &seg1.edge1;
  820. outer2 = &seg2.edge1;
  821. inner1 = &seg1.edge2;
  822. inner2 = &seg2.edge2;
  823. } else {
  824. outer1 = &seg1.edge2;
  825. outer2 = &seg2.edge2;
  826. inner1 = &seg1.edge1;
  827. inner2 = &seg2.edge1;
  828. }
  829. // calculate the intersection point of the inner edges
  830. gs_vg_line_seg_intersection_t inner_sec_opt = gs_vg_line_seg_intersection(*inner1, *inner2, allow_overlap);
  831. gs_vg_point_t inner_sec = inner_sec_opt.intersected
  832. ? inner_sec_opt.point
  833. // for parallel lines, simply connect them directly
  834. : inner1->b;
  835. // if there's no inner intersection, flip
  836. // the next start position for near-180° turns
  837. gs_vg_point_t inner_start;
  838. if (inner_sec_opt.intersected)
  839. {
  840. inner_start = inner_sec;
  841. }
  842. else if (angle > GS_PI / 2.0)
  843. {
  844. inner_start = outer1->b;
  845. }
  846. else
  847. {
  848. inner_start = inner1->b;
  849. }
  850. if (clockwise)
  851. {
  852. *end1 = outer1->b;
  853. *end2 = inner_sec;
  854. *next_start1 = outer2->a;
  855. *next_start2 = inner_start;
  856. }
  857. else
  858. {
  859. *end1 = inner_sec;
  860. *end2 = outer1->b;
  861. *next_start1 = inner_start;
  862. *next_start2 = outer2->a;
  863. }
  864. // connect the intersection points according to the joint style
  865. if (joint_style == GS_VG_JOINT_BEVEL)
  866. {
  867. gsi_c4ubv(&ctx->gsi, outer1->b.color);
  868. gsi_v2fv(&ctx->gsi, outer1->b.position);
  869. gsi_c4ubv(&ctx->gsi, outer2->a.color);
  870. gsi_v2fv(&ctx->gsi, outer2->a.position);
  871. gsi_c4ubv(&ctx->gsi, inner_sec.color);
  872. gsi_v2fv(&ctx->gsi, inner_sec.position);
  873. float anti_alias_scl = paint->aa_scale;
  874. if (paint->anti_alias)
  875. {
  876. gs_vec2 ns = gs_vec2_norm(gs_vec2_sub(outer1->b.position, inner_sec.position));
  877. gs_vec2 ne = gs_vec2_norm(gs_vec2_sub(outer2->a.position, inner_sec.position));
  878. // gs_vec4 s_col = (gs_vec4){outer1->b.color.x, outer1->b.color.y, outer1->b.color.z, 0.f};
  879. // gs_vec4 e_col = (gs_vec4){outer2->a.color.x, outer2->a.color.y, outer2->a.color.z, 0.f};
  880. gs_color_t s_col = gs_color_alpha(outer1->b.color, 0);
  881. gs_color_t e_col = gs_color_alpha(outer2->a.color, 0);
  882. const f32 sl = (gs_vec2_len(gs_vec2_sub(outer1->b.position, inner_sec.position)) / (outer1->b.thickness)) * anti_alias_scl;
  883. const f32 el = (gs_vec2_len(gs_vec2_sub(outer2->a.position, inner_sec.position)) / (outer2->a.thickness)) * anti_alias_scl;
  884. gs_vg_point_t s = gs_vg_point_add(outer1->b, gs_vec2_scale(ns, sl));
  885. gs_vg_point_t e = gs_vg_point_add(outer2->a, gs_vec2_scale(ns, el));
  886. gsi_c4ubv(&ctx->gsi, s_col); gsi_v2fv(&ctx->gsi, s.position);
  887. gsi_c4ubv(&ctx->gsi, outer1->b.color); gsi_v2fv(&ctx->gsi, outer1->b.position);
  888. gsi_c4ubv(&ctx->gsi, e_col); gsi_v2fv(&ctx->gsi, e.position);
  889. gsi_c4ubv(&ctx->gsi, e_col); gsi_v2fv(&ctx->gsi, e.position);
  890. gsi_c4ubv(&ctx->gsi, outer2->a.color); gsi_v2fv(&ctx->gsi, outer2->a.position);
  891. gsi_c4ubv(&ctx->gsi, outer1->b.color); gsi_v2fv(&ctx->gsi, outer1->b.position);
  892. }
  893. }
  894. else if (joint_style == GS_VG_JOINT_ROUND)
  895. {
  896. // draw a circle between the ends of the outer edges,
  897. // centered at the actual point
  898. // with half the line thickness as the radius
  899. _gsvg_path_stroke_triangle_fan_impl(ctx, inner_sec, seg1.center.b, outer1->b, outer2->a, clockwise);
  900. }
  901. else
  902. {
  903. gs_assert(false);
  904. }
  905. }
  906. }
  907. typedef struct
  908. {
  909. uint16_t indices[3];
  910. } triangle_t;
  911. GS_API_DECL void _gsvg_path_fill_impl(gs_vg_ctx_t* ctx)
  912. {
  913. // Ear clipping, naive, no holes filled
  914. // Generate index and triangle lists to fill
  915. // All indices will just be entire point list
  916. gs_dyn_array(uint16_t) points = NULL;
  917. gs_dyn_array(triangle_t) triangles = NULL;
  918. }
  919. #endif // GS_VG_IMPL
  920. #endif // GS_VG_H