test_shapes.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include "describe.h"
  2. #define PAR_SHAPES_IMPLEMENTATION
  3. #include "par_shapes.h"
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #define STRINGIFY(A) #A
  7. int main()
  8. {
  9. describe("cylinders and spheres") {
  10. it("should fail when the number of stacks or slices is invalid") {
  11. par_shapes_mesh* bad1 = par_shapes_create_cylinder(1, 1);
  12. par_shapes_mesh* bad2 = par_shapes_create_cylinder(1, 3);
  13. par_shapes_mesh* good = par_shapes_create_cylinder(3, 1);
  14. assert_null(bad1);
  15. assert_null(bad2);
  16. assert_ok(good);
  17. par_shapes_free_mesh(good);
  18. }
  19. it("should generate correct number of vertices") {
  20. par_shapes_mesh* m = par_shapes_create_cylinder(5, 6);
  21. assert_equal(m->npoints, 42);
  22. par_shapes_free_mesh(m);
  23. }
  24. it("should have expected number of triangles") {
  25. par_shapes_mesh* m;
  26. int slices, stacks ;
  27. slices = 5; stacks = 6;
  28. m = par_shapes_create_cylinder(slices, stacks);
  29. assert_equal(m->ntriangles, slices * stacks * 2);
  30. par_shapes_free_mesh(m);
  31. slices = 5; stacks = 6;
  32. m = par_shapes_create_parametric_sphere(slices, stacks);
  33. assert_equal(m->ntriangles, slices * 2 + (stacks - 2) * slices * 2);
  34. par_shapes_free_mesh(m);
  35. slices = 12; stacks = 13;
  36. m = par_shapes_create_parametric_sphere(slices, stacks);
  37. assert_equal(m->ntriangles, slices * 2 + (stacks - 2) * slices * 2);
  38. par_shapes_free_mesh(m);
  39. slices = 16; stacks = 16;
  40. m = par_shapes_create_parametric_sphere(slices, stacks);
  41. assert_equal(m->ntriangles, slices * 2 + (stacks - 2) * slices * 2);
  42. par_shapes_free_mesh(m);
  43. }
  44. }
  45. describe("par_shapes_create_plane") {
  46. it("should not have NaN's") {
  47. par_shapes_mesh* m = par_shapes_create_plane(5, 6);
  48. for (int i = 0; i < m->npoints * 3; i++) {
  49. assert_ok(m->points[i] == m->points[i]);
  50. assert_ok(m->normals[i] == m->normals[i]);
  51. }
  52. par_shapes_free_mesh(m);
  53. }
  54. }
  55. describe("par_shapes_export") {
  56. it("should generate an OBJ file") {
  57. par_shapes_mesh* m;
  58. m = par_shapes_create_torus(7, 10, 0.5);
  59. par_shapes_export(m, "build/test_shapes_torus.obj");
  60. par_shapes_free_mesh(m);
  61. m = par_shapes_create_subdivided_sphere(2);
  62. par_shapes_export(m, "build/test_shapes_ssphere.obj");
  63. par_shapes_free_mesh(m);
  64. m = par_shapes_create_klein_bottle(10, 20);
  65. par_shapes_export(m, "build/test_shapes_klein.obj");
  66. par_shapes_free_mesh(m);
  67. m = par_shapes_create_trefoil_knot(20, 100, 0.5);
  68. par_shapes_export(m, "build/test_shapes_trefoil.obj");
  69. par_shapes_free_mesh(m);
  70. m = par_shapes_create_hemisphere(5, 6);
  71. par_shapes_export(m, "build/test_shapes_hemisphere.obj");
  72. par_shapes_free_mesh(m);
  73. m = par_shapes_create_icosahedron();
  74. par_shapes_export(m, "build/test_shapes_icosahedron.obj");
  75. par_shapes_free_mesh(m);
  76. m = par_shapes_create_dodecahedron();
  77. par_shapes_export(m, "build/test_shapes_dodecahedron.obj");
  78. par_shapes_free_mesh(m);
  79. m = par_shapes_create_octahedron();
  80. par_shapes_export(m, "build/test_shapes_octahedron.obj");
  81. par_shapes_free_mesh(m);
  82. m = par_shapes_create_tetrahedron();
  83. par_shapes_export(m, "build/test_shapes_tetrahedron.obj");
  84. par_shapes_free_mesh(m);
  85. m = par_shapes_create_cube();
  86. par_shapes_export(m, "build/test_shapes_cube.obj");
  87. par_shapes_free_mesh(m);
  88. m = par_shapes_create_rock(1, 3);
  89. par_shapes_export(m, "build/test_shapes_rock.obj");
  90. par_shapes_free_mesh(m);
  91. m = par_shapes_create_cone(15, 3);
  92. par_shapes_export(m, "build/test_shapes_cone.obj");
  93. par_shapes_free_mesh(m);
  94. m = par_shapes_create_parametric_disk(15, 3);
  95. par_shapes_export(m, "build/test_shapes_parametric_disk.obj");
  96. par_shapes_free_mesh(m);
  97. float center[3] = {0, 0, 0};
  98. float normal[3] = {0, 0, 1};
  99. m = par_shapes_create_disk(1, 5, center, normal);
  100. par_shapes_export(m, "build/test_shapes_disk.obj");
  101. par_shapes_free_mesh(m);
  102. }
  103. }
  104. describe("par_shapes_merge") {
  105. it("should concatenate two meshes") {
  106. par_shapes_mesh* a, *b;
  107. a = par_shapes_create_klein_bottle(10, 20);
  108. int npts = a->npoints;
  109. int ntris = a->ntriangles;
  110. b = par_shapes_create_plane(3, 3);
  111. par_shapes_merge(a, b);
  112. assert_equal(a->npoints, npts + b->npoints);
  113. assert_equal(a->ntriangles, ntris + b->ntriangles);
  114. par_shapes_free_mesh(a);
  115. par_shapes_free_mesh(b);
  116. }
  117. }
  118. describe("transforms") {
  119. it("should support translation") {
  120. par_shapes_mesh* a, *b;
  121. a = par_shapes_create_cylinder(20, 3);
  122. b = par_shapes_create_cylinder(4, 3);
  123. par_shapes_translate(a, 0.5, 0.5, 0.25);
  124. par_shapes_merge(a, b);
  125. par_shapes_free_mesh(a);
  126. par_shapes_free_mesh(b);
  127. }
  128. it("should support rotation") {
  129. par_shapes_mesh* a, *b;
  130. a = par_shapes_create_cylinder(20, 3);
  131. b = par_shapes_create_cylinder(4, 3);
  132. float axis1[3] = {0, 1, 0};
  133. float axis2[3] = {0, 0, 1};
  134. par_shapes_rotate(a, PAR_PI * 0.5, axis1);
  135. par_shapes_rotate(a, PAR_PI * 0.25, axis2);
  136. par_shapes_merge(a, b);
  137. par_shapes_free_mesh(a);
  138. par_shapes_free_mesh(b);
  139. }
  140. it("should support non-uniform scale") {
  141. par_shapes_mesh* a;
  142. a = par_shapes_create_cylinder(15, 3);
  143. par_shapes_scale(a, 1, 1, 5);
  144. par_shapes_free_mesh(a);
  145. }
  146. it("should support degenerate scale") {
  147. par_shapes_mesh* a;
  148. a = par_shapes_create_cone(15, 3);
  149. assert_ok(a);
  150. par_shapes_scale(a, 1, 1, 0);
  151. for (int i = 0; i < a->npoints * 3; i++) {
  152. // should not have nans
  153. assert_ok(a->points[i] == a->points[i]);
  154. assert_ok(a->normals[i] == a->normals[i]);
  155. // check components
  156. if (i % 3 != 2) {
  157. assert_ok(a->normals[i] == 0.0f);
  158. } else {
  159. assert_ok(a->normals[i] == 1.0f);
  160. assert_ok(a->points[i] == 0.0f);
  161. }
  162. }
  163. par_shapes_free_mesh(a);
  164. }
  165. }
  166. describe("misc shapes") {
  167. it("create an orientable disk in 3-space") {
  168. int slices = 32;
  169. float aradius = 1;
  170. float anormal[3] = {0, 0, 1};
  171. float acenter[3] = {0, 0, 0};
  172. par_shapes_mesh* a, *b;
  173. a = par_shapes_create_disk(aradius, slices, acenter, anormal);
  174. float bradius = 0.2;
  175. float bcenter[3] = {0, 0, 0.2};
  176. float bnormal[3] = {0, 1, 0};
  177. b = par_shapes_create_disk(bradius, slices, bcenter, bnormal);
  178. par_shapes_merge(a, b);
  179. par_shapes_free_mesh(a);
  180. par_shapes_free_mesh(b);
  181. }
  182. it("create a rock on the Y plane") {
  183. int slices = 32;
  184. float radius = 2;
  185. float normal[3] = {0, 1, 0};
  186. float center[3] = {0, 0, 0};
  187. par_shapes_mesh* a, *b;
  188. a = par_shapes_create_disk(radius, slices, center, normal);
  189. b = par_shapes_create_rock(1, 2);
  190. float aabb[6];
  191. par_shapes_compute_aabb(b, aabb);
  192. par_shapes_translate(b, 0, -aabb[1] / 2, 0);
  193. par_shapes_merge(a, b);
  194. par_shapes_free_mesh(a);
  195. par_shapes_free_mesh(b);
  196. }
  197. it("create a polyhedron on the Y plane") {
  198. int slices = 32;
  199. float radius = 2;
  200. float normal[3] = {0, 1, 0};
  201. float center[3] = {0, 0, 0};
  202. par_shapes_mesh* a, *b;
  203. a = par_shapes_create_disk(radius, slices, center, normal);
  204. b = par_shapes_create_dodecahedron();
  205. par_shapes_translate(b, 0, 0.934, 0);
  206. par_shapes_merge(a, b);
  207. par_shapes_free_mesh(a);
  208. par_shapes_free_mesh(b);
  209. }
  210. it("create a rounded cylinder via composition") {
  211. const float O[3] = {0, 0, 0};
  212. const float I[3] = {1, 0, 0};
  213. const float J[3] = {0, 1, 0};
  214. const float K[3] = {0, 0, 1};
  215. const float top_center[3] = {0, 1.2, 0};
  216. const int tess = 30;
  217. par_shapes_mesh *a, *b, *c, *d;
  218. a = par_shapes_create_disk(2.5, tess, O, J);
  219. b = par_shapes_create_cylinder(tess, 3);
  220. c = par_shapes_create_torus(15, tess, 0.1);
  221. d = par_shapes_create_disk(1, tess, top_center, J);
  222. par_shapes_rotate(c, PAR_PI / tess, K);
  223. par_shapes_translate(c, 0, 0, 1);
  224. par_shapes_scale(b, 1.2, 1.2, 1);
  225. par_shapes_merge(b, c);
  226. par_shapes_rotate(b, -PAR_PI * 0.5, I);
  227. par_shapes_merge(b, d);
  228. par_shapes_merge(b, a);
  229. par_shapes_scale(b, 1, 2, 1);
  230. par_shapes_free_mesh(a);
  231. par_shapes_free_mesh(b);
  232. par_shapes_free_mesh(c);
  233. par_shapes_free_mesh(d);
  234. }
  235. }
  236. describe("lsystems") {
  237. it("export a tree-like shape") {
  238. char const* program = STRINGIFY(
  239. sx 2 sy 2
  240. ry 90 rx 90
  241. shape tube rx 15 call rlimb rx -15
  242. shape tube rx -15 call llimb rx 15
  243. shape tube ry 15 call rlimb ry -15
  244. shape tube ry 15 call llimb ry -15
  245. rule rlimb
  246. sx 0.925 sy 0.925 tz 1 rx 1.2
  247. call rlimb2
  248. rule rlimb2.1
  249. shape connect
  250. call rlimb
  251. rule rlimb2.1
  252. rx 15 shape tube call rlimb rx -15
  253. rx -15 shape tube call llimb rx 15
  254. rule rlimb.1
  255. call llimb
  256. rule llimb.1
  257. call rlimb
  258. rule llimb.10
  259. sx 0.925 sy 0.925
  260. tz 1
  261. rx -1.2
  262. shape connect
  263. call llimb
  264. );
  265. const float O[3] = {0, 0, 0};
  266. const float J[3] = {0, 1, 0};
  267. par_shapes_mesh* mesh = par_shapes_create_lsystem(program, 5, 60);
  268. par_shapes_mesh* disk = par_shapes_create_disk(10, 30, O, J);
  269. par_shapes_merge(mesh, disk);
  270. par_shapes_free_mesh(disk);
  271. par_shapes_export(mesh, "build/lsystem.obj");
  272. par_shapes_free_mesh(mesh);
  273. }
  274. }
  275. return assert_failures();
  276. }