test_bubbles.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #include "describe.h"
  2. #define PAR_BUBBLES_IMPLEMENTATION
  3. #include "par_bubbles.h"
  4. #define PAR_SHAPES_T uint32_t
  5. #define PAR_SHAPES_IMPLEMENTATION
  6. #include "par_shapes.h"
  7. static par_bubbles_t* bubbles;
  8. #define NRADIUSES 100
  9. static double radiuses[NRADIUSES];
  10. #define NNODES 252
  11. static int hierarchy[NNODES] = {
  12. 0, 0, 1, 2, 2, 2, 2, 1, 7, 7, 7, 7,
  13. 7, 1, 13, 0, 15, 15, 15, 18, 18, 18, 18, 18,
  14. 18, 18, 18, 18, 15, 15, 15, 15, 15, 15, 15, 15,
  15. 15, 0, 37, 38, 38, 38, 38, 38, 37, 37, 37, 37,
  16. 37, 37, 0, 50, 50, 50, 50, 0, 55, 0, 57, 57,
  17. 57, 57, 57, 57, 57, 57, 0, 66, 66, 66, 66, 66,
  18. 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
  19. 66, 66, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
  20. 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
  21. 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 66, 66,
  22. 66, 66, 66, 66, 66, 66, 66, 66, 0, 128, 128, 128,
  23. 128, 128, 128, 128, 128, 128, 128, 0, 139, 139, 139, 139,
  24. 139, 139, 139, 146, 146, 139, 139, 139, 139, 152, 152, 152,
  25. 139, 139, 139, 158, 158, 158, 158, 139, 139, 139, 139, 139,
  26. 0, 168, 169, 169, 169, 169, 169, 168, 175, 175, 175, 175,
  27. 175, 175, 175, 175, 175, 175, 175, 168, 187, 187, 187, 187,
  28. 187, 187, 193, 193, 193, 193, 187, 187, 187, 168, 201, 201,
  29. 201, 201, 168, 206, 206, 206, 168, 210, 211, 211, 211, 210,
  30. 215, 215, 215, 215, 215, 210, 221, 221, 221, 210, 210, 226,
  31. 226, 226, 210, 230, 230, 230, 230, 230, 230, 230, 230, 230,
  32. 230, 230, 230, 230, 230, 230, 210, 210, 210, 210, 210, 168,
  33. };
  34. static int get_depth(int* tree, int i)
  35. {
  36. int d = 0;
  37. while (i) {
  38. i = tree[i];
  39. d++;
  40. }
  41. return d;
  42. }
  43. int main()
  44. {
  45. for (int i = 0; i < NRADIUSES; i++) {
  46. radiuses[i] = 1 + rand() % 10;
  47. }
  48. describe("par_bubbles_pack") {
  49. it("should pass a simple smoke test") {
  50. bubbles = par_bubbles_pack(radiuses, NRADIUSES);
  51. assert_ok(bubbles);
  52. assert_equal(bubbles->count, (int) NRADIUSES);
  53. par_bubbles_export(bubbles, "build/test_bubbles_pack.svg");
  54. par_bubbles_free_result(bubbles);
  55. }
  56. it("should handle a small number of nodes") {
  57. bubbles = par_bubbles_pack(radiuses, 0);
  58. par_bubbles_export(bubbles, "build/test_bubbles_pack0.svg");
  59. par_bubbles_free_result(bubbles);
  60. bubbles = par_bubbles_pack(radiuses, 1);
  61. par_bubbles_export(bubbles, "build/test_bubbles_pack1.svg");
  62. par_bubbles_free_result(bubbles);
  63. bubbles = par_bubbles_pack(radiuses, 2);
  64. par_bubbles_export(bubbles, "build/test_bubbles_pack2.svg");
  65. par_bubbles_free_result(bubbles);
  66. bubbles = par_bubbles_pack(radiuses, 3);
  67. par_bubbles_export(bubbles, "build/test_bubbles_pack3.svg");
  68. par_bubbles_free_result(bubbles);
  69. }
  70. it("should work with small hierarchy") {
  71. static int hierarchy[10] = {
  72. 0, 0, 0, 0, 1, 1, 2,
  73. 5, 5, 5,
  74. };
  75. bubbles = par_bubbles_hpack_circle(hierarchy, 10, 100);
  76. par_bubbles_export(bubbles, "build/test_bubbles_hpack_circle1.svg");
  77. par_bubbles_free_result(bubbles);
  78. }
  79. it("can be exported to an SVG") {
  80. bubbles = par_bubbles_hpack_circle(hierarchy, NNODES, 100);
  81. par_bubbles_export(bubbles, "build/test_bubbles_hpack_circle2.svg");
  82. par_bubbles_free_result(bubbles);
  83. }
  84. it("can be exported to an OBJ") {
  85. const int nnodes = 2e3;
  86. const float zscale = 0.01;
  87. // First, generate a random tree. Square the random parent pointers
  88. // to make the graph distribution a bit more interesting, and to
  89. // make it easier for humans to find deep portions of the tree.
  90. int* tree = malloc(sizeof(int) * nnodes);
  91. tree[0] = 0;
  92. for (int i = 1; i < nnodes; i++) {
  93. float a = (float) rand() / RAND_MAX;
  94. float b = (float) rand() / RAND_MAX;
  95. tree[i] = i * a * b;
  96. }
  97. // Perform circle packing.
  98. bubbles = par_bubbles_hpack_circle(tree, nnodes, 1.0);
  99. // Create template shape.
  100. float normal[3] = {0, 0, 1};
  101. float center[3] = {0, 0, 0};
  102. par_shapes_mesh* template = par_shapes_create_disk(1.0, 64,
  103. center, normal);
  104. // Merge each circle into the scene.
  105. par_shapes_mesh* scene = par_shapes_create_empty();
  106. double const* xyr = bubbles->xyr;
  107. par_shapes_mesh* clone = 0;
  108. for (int i = 0; i < bubbles->count; i++, xyr += 3) {
  109. float d = get_depth(tree, i);
  110. clone = par_shapes_clone(template, clone);
  111. par_shapes_scale(clone, xyr[2], xyr[2], 1.0);
  112. par_shapes_translate(clone, xyr[0], xyr[1], d * zscale);
  113. par_shapes_merge(scene, clone);
  114. }
  115. // Export the OBJ file.
  116. char const* const filename = "build/bubbles.obj";
  117. par_shapes_export(scene, filename);
  118. // Free memory.
  119. par_shapes_free_mesh(template);
  120. par_shapes_free_mesh(clone);
  121. par_shapes_free_mesh(scene);
  122. par_bubbles_free_result(bubbles);
  123. free(tree);
  124. }
  125. }
  126. describe("precision") {
  127. #define H1 10
  128. #define H2 20
  129. it("works well with relative coordinate systems") {
  130. bubbles = par_bubbles_hpack_local(hierarchy, NNODES);
  131. par_bubbles_export_local(bubbles, 0,
  132. "build/test_bubbles_hpack_local1.svg");
  133. par_bubbles_export_local(bubbles, 158,
  134. "build/test_bubbles_hpack_local2.svg");
  135. par_bubbles_free_result(bubbles);
  136. }
  137. it("is poor with deep nesting and non-local packing") {
  138. static int hierarchy[H2] = {0};
  139. for (int i = 1; i < H1; i++) {
  140. hierarchy[i] = i - 1;
  141. }
  142. for (int i = H1; i < H2; i++) {
  143. hierarchy[i] = H1 - 1;
  144. }
  145. bubbles = par_bubbles_hpack_circle(hierarchy, H2, 1);
  146. par_bubbles_export_local(bubbles, H1 - 1,
  147. "build/test_bubbles_hpack_local3.svg");
  148. par_bubbles_free_result(bubbles);
  149. }
  150. it("is great with deep nesting and local packing") {
  151. static int hierarchy[H2] = {0};
  152. for (int i = 1; i < H1; i++) {
  153. hierarchy[i] = i - 1;
  154. }
  155. for (int i = H1; i < H2; i++) {
  156. hierarchy[i] = H1 - 1;
  157. }
  158. bubbles = par_bubbles_hpack_local(hierarchy, H2);
  159. par_bubbles_export_local(bubbles, H1 - 1,
  160. "build/test_bubbles_hpack_local4.svg");
  161. par_bubbles_free_result(bubbles);
  162. }
  163. #undef H1
  164. #undef H2
  165. }
  166. describe("par_bubbles_find_local") {
  167. it("finds the smallest node that completely encloses a box") {
  168. bubbles = par_bubbles_hpack_local(hierarchy, NNODES);
  169. // This aabb encloses node 158, which means node 139 is the deepest
  170. // circle that encloses it.
  171. double cx = 40.804406 / 100.0f;
  172. double cy = -15.209295 / 100.0f;
  173. double r = 8.133015 / 100.0f;
  174. double aabb[4] = { cx - r, cy - r, cx + r, cy + r };
  175. int node = par_bubbles_find_local(bubbles, aabb, 0);
  176. par_bubbles_free_result(bubbles);
  177. assert_equal(node, 139);
  178. }
  179. it("ditto, but using a non-root coordinate system") {
  180. bubbles = par_bubbles_hpack_local(hierarchy, NNODES);
  181. par_bubbles_export_local(bubbles, 139,
  182. "build/test_bubbles_hpack_local5.svg");
  183. double cx = -0.511578, cy = 0.147831, r = 0.08;
  184. double aabb[4] = { cx - r, cy - r, cx + r, cy + r };
  185. int node = par_bubbles_find_local(bubbles, aabb, 139);
  186. par_bubbles_free_result(bubbles);
  187. assert_equal(node, 158);
  188. }
  189. it("is used by the pick_local function") {
  190. bubbles = par_bubbles_hpack_local(hierarchy, NNODES);
  191. double cx = -0.654258, cy = 0.065455;
  192. double minradius = 0;
  193. int node = par_bubbles_pick_local(bubbles, cx, cy, 139, minradius);
  194. assert_equal(node, 162);
  195. minradius = 0.095;
  196. node = par_bubbles_pick_local(bubbles, cx, cy, 139, minradius);
  197. assert_equal(node, 158);
  198. par_bubbles_free_result(bubbles);
  199. }
  200. }
  201. return assert_failures();
  202. }