test_shapes.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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_export") {
  46. it("should generate an OBJ file") {
  47. par_shapes_mesh* m;
  48. m = par_shapes_create_torus(7, 10, 0.5);
  49. par_shapes_export(m, "build/test_shapes_torus.obj");
  50. par_shapes_free_mesh(m);
  51. m = par_shapes_create_subdivided_sphere(2);
  52. par_shapes_export(m, "build/test_shapes_ssphere.obj");
  53. par_shapes_free_mesh(m);
  54. m = par_shapes_create_klein_bottle(10, 20);
  55. par_shapes_export(m, "build/test_shapes_klein.obj");
  56. par_shapes_free_mesh(m);
  57. m = par_shapes_create_trefoil_knot(20, 100, 0.5);
  58. par_shapes_export(m, "build/test_shapes_trefoil.obj");
  59. par_shapes_free_mesh(m);
  60. m = par_shapes_create_hemisphere(5, 6);
  61. par_shapes_export(m, "build/test_shapes_hemisphere.obj");
  62. par_shapes_free_mesh(m);
  63. m = par_shapes_create_plane(5, 6);
  64. par_shapes_export(m, "build/test_shapes_plane.obj");
  65. par_shapes_free_mesh(m);
  66. m = par_shapes_create_icosahedron();
  67. par_shapes_export(m, "build/test_shapes_icosahedron.obj");
  68. par_shapes_free_mesh(m);
  69. m = par_shapes_create_dodecahedron();
  70. par_shapes_export(m, "build/test_shapes_dodecahedron.obj");
  71. par_shapes_free_mesh(m);
  72. m = par_shapes_create_octohedron();
  73. par_shapes_export(m, "build/test_shapes_octohedron.obj");
  74. par_shapes_free_mesh(m);
  75. m = par_shapes_create_tetrahedron();
  76. par_shapes_export(m, "build/test_shapes_tetrahedron.obj");
  77. par_shapes_free_mesh(m);
  78. m = par_shapes_create_cube();
  79. par_shapes_export(m, "build/test_shapes_cube.obj");
  80. par_shapes_free_mesh(m);
  81. m = par_shapes_create_rock(1, 3);
  82. par_shapes_export(m, "build/test_shapes_rock.obj");
  83. par_shapes_free_mesh(m);
  84. float center[3] = {0, 0, 0};
  85. float normal[3] = {0, 0, 1};
  86. m = par_shapes_create_disk(1, 5, center, normal);
  87. par_shapes_export(m, "build/test_shapes_disk.obj");
  88. par_shapes_free_mesh(m);
  89. }
  90. }
  91. describe("par_shapes_merge") {
  92. it("should concatenate two meshes") {
  93. par_shapes_mesh* a, *b;
  94. a = par_shapes_create_klein_bottle(10, 20);
  95. int npts = a->npoints;
  96. int ntris = a->ntriangles;
  97. b = par_shapes_create_plane(3, 3);
  98. par_shapes_merge(a, b);
  99. assert_equal(a->npoints, npts + b->npoints);
  100. assert_equal(a->ntriangles, ntris + b->ntriangles);
  101. par_shapes_free_mesh(a);
  102. par_shapes_free_mesh(b);
  103. }
  104. }
  105. describe("transforms") {
  106. it("should support translation") {
  107. par_shapes_mesh* a, *b;
  108. a = par_shapes_create_cylinder(20, 3);
  109. b = par_shapes_create_cylinder(4, 3);
  110. par_shapes_translate(a, 0.5, 0.5, 0.25);
  111. par_shapes_merge(a, b);
  112. par_shapes_free_mesh(a);
  113. par_shapes_free_mesh(b);
  114. }
  115. it("should support rotation") {
  116. par_shapes_mesh* a, *b;
  117. a = par_shapes_create_cylinder(20, 3);
  118. b = par_shapes_create_cylinder(4, 3);
  119. float axis1[3] = {0, 1, 0};
  120. float axis2[3] = {0, 0, 1};
  121. par_shapes_rotate(a, PAR_PI * 0.5, axis1);
  122. par_shapes_rotate(a, PAR_PI * 0.25, axis2);
  123. par_shapes_merge(a, b);
  124. par_shapes_free_mesh(a);
  125. par_shapes_free_mesh(b);
  126. }
  127. it("should support non-uniform scale") {
  128. par_shapes_mesh* a;
  129. a = par_shapes_create_cylinder(15, 3);
  130. par_shapes_scale(a, 1, 1, 5);
  131. par_shapes_free_mesh(a);
  132. }
  133. }
  134. describe("misc shapes") {
  135. it("create an orientable disk in 3-space") {
  136. int slices = 32;
  137. float aradius = 1;
  138. float anormal[3] = {0, 0, 1};
  139. float acenter[3] = {0, 0, 0};
  140. par_shapes_mesh* a, *b;
  141. a = par_shapes_create_disk(aradius, slices, acenter, anormal);
  142. float bradius = 0.2;
  143. float bcenter[3] = {0, 0, 0.2};
  144. float bnormal[3] = {0, 1, 0};
  145. b = par_shapes_create_disk(bradius, slices, bcenter, bnormal);
  146. par_shapes_merge(a, b);
  147. par_shapes_free_mesh(a);
  148. par_shapes_free_mesh(b);
  149. }
  150. it("create a rock on the Y plane") {
  151. int slices = 32;
  152. float radius = 2;
  153. float normal[3] = {0, 1, 0};
  154. float center[3] = {0, 0, 0};
  155. par_shapes_mesh* a, *b;
  156. a = par_shapes_create_disk(radius, slices, center, normal);
  157. b = par_shapes_create_rock(1, 2);
  158. float aabb[6];
  159. par_shapes_compute_aabb(b, aabb);
  160. par_shapes_translate(b, 0, -aabb[1] / 2, 0);
  161. par_shapes_merge(a, b);
  162. par_shapes_free_mesh(a);
  163. par_shapes_free_mesh(b);
  164. }
  165. it("create a polyhedron on the Y plane") {
  166. int slices = 32;
  167. float radius = 2;
  168. float normal[3] = {0, 1, 0};
  169. float center[3] = {0, 0, 0};
  170. par_shapes_mesh* a, *b;
  171. a = par_shapes_create_disk(radius, slices, center, normal);
  172. b = par_shapes_create_dodecahedron();
  173. par_shapes_translate(b, 0, 0.934, 0);
  174. par_shapes_merge(a, b);
  175. par_shapes_free_mesh(a);
  176. par_shapes_free_mesh(b);
  177. }
  178. it("create a rounded cylinder via composition") {
  179. const float O[3] = {0, 0, 0};
  180. const float I[3] = {1, 0, 0};
  181. const float J[3] = {0, 1, 0};
  182. const float K[3] = {0, 0, 1};
  183. const float top_center[3] = {0, 1.2, 0};
  184. const int tess = 30;
  185. par_shapes_mesh *a, *b, *c, *d;
  186. a = par_shapes_create_disk(2.5, tess, O, J);
  187. b = par_shapes_create_cylinder(tess, 3);
  188. c = par_shapes_create_torus(15, tess, 0.1);
  189. d = par_shapes_create_disk(1, tess, top_center, J);
  190. par_shapes_rotate(c, PAR_PI / tess, K);
  191. par_shapes_translate(c, 0, 0, 1);
  192. par_shapes_scale(b, 1.2, 1.2, 1);
  193. par_shapes_merge(b, c);
  194. par_shapes_rotate(b, -PAR_PI * 0.5, I);
  195. par_shapes_merge(b, d);
  196. par_shapes_merge(b, a);
  197. par_shapes_scale(b, 1, 2, 1);
  198. par_shapes_free_mesh(a);
  199. par_shapes_free_mesh(b);
  200. par_shapes_free_mesh(c);
  201. par_shapes_free_mesh(d);
  202. }
  203. }
  204. describe("lsystems") {
  205. it("export a tree-like shape") {
  206. char const* program = STRINGIFY(
  207. sx 2 sy 2
  208. ry 90 rx 90
  209. shape tube rx 15 call rlimb rx -15
  210. shape tube rx -15 call llimb rx 15
  211. shape tube ry 15 call rlimb ry -15
  212. shape tube ry 15 call llimb ry -15
  213. rule rlimb
  214. sx 0.925 sy 0.925 tz 1 rx 1.2
  215. call rlimb2
  216. rule rlimb2.1
  217. shape connect
  218. call rlimb
  219. rule rlimb2.1
  220. rx 15 shape tube call rlimb rx -15
  221. rx -15 shape tube call llimb rx 15
  222. rule rlimb.1
  223. call llimb
  224. rule llimb.1
  225. call rlimb
  226. rule llimb.10
  227. sx 0.925 sy 0.925
  228. tz 1
  229. rx -1.2
  230. shape connect
  231. call llimb
  232. );
  233. const float O[3] = {0, 0, 0};
  234. const float J[3] = {0, 1, 0};
  235. par_shapes_mesh* mesh = par_shapes_create_lsystem(program, 5, 60);
  236. par_shapes_mesh* disk = par_shapes_create_disk(10, 30, O, J);
  237. par_shapes_merge(mesh, disk);
  238. par_shapes_export(mesh, "build/lsystem.obj");
  239. par_shapes_free_mesh(mesh);
  240. }
  241. }
  242. return assert_failures();
  243. }