par_bubbles.h 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. // BUBBLES :: https://github.com/prideout/par
  2. // Simple C library for packing circles into hierarchical (or flat) diagrams.
  3. //
  4. // Based on "Visualization of Large Hierarchical Data by Circle Packing" by
  5. // Wang et al (2006), with an extension for cylinderical space.
  6. //
  7. // Also implements Emo Welzl's "Smallest enclosing disks" algorithm (1991).
  8. //
  9. // The API is divided into three sections:
  10. //
  11. // - Enclosing. Compute the smallest bounding circle for points or circles.
  12. // - Packing. Pack circles together, or into other circles.
  13. // - Queries. Given a touch point, pick a circle from a hierarchy, etc.
  14. //
  15. // In addition to the comment block above each function declaration, the API
  16. // has informal documentation here:
  17. //
  18. // http://github.prideout.net/bubbles/
  19. //
  20. // The MIT License
  21. // Copyright (c) 2015 Philip Rideout
  22. #ifndef PAR_BUBBLES_H
  23. #define PAR_BUBBLES_H
  24. #include <stdbool.h>
  25. // Enclosing / Touching --------------------------------------------------------
  26. // Read an array of (x,y) coordinates, write a single 3-tuple (x,y,radius).
  27. void par_bubbles_enclose_points(double const* xy, int npts, double* result);
  28. // Read an array of 3-tuples (x,y,radius), write a 3-tuple (x,y,radius).
  29. // Internally, this approximates each disk with an enclosing octagon.
  30. void par_bubbles_enclose_disks(double const* xyr, int ndisks, double* result);
  31. // Find the circle (x,y,radius) that is tangent to 3 points (x,y).
  32. void par_bubbles_touch_three_points(double const* xy, double* result);
  33. // Find a position for disk "c" that makes it tangent to "a" and "b".
  34. // Note that the ordering of a and b can affect where c will land.
  35. // All three arguments are pointers to three-tuples (x,y,radius).
  36. void par_bubbles_touch_two_disks(double* c, double const* a, double const* b);
  37. // Packing ---------------------------------------------------------------------
  38. // Tiny POD structure returned by all packing functions. Private data is
  39. // attached after the public fields, so clients should call the provided
  40. // free function rather than freeing the memory manually.
  41. typedef struct {
  42. double* xyr; // array of 3-tuples (x y radius) in same order as input data
  43. int count; // number of 3-tuples in "xyr"
  44. int* ids; // if non-null, filled by par_bubbles_cull with a mapping table
  45. } par_bubbles_t;
  46. void par_bubbles_free_result(par_bubbles_t*);
  47. // Entry point for unbounded non-hierarchical packing. Takes a list of radii.
  48. par_bubbles_t* par_bubbles_pack(double const* radiuses, int nradiuses);
  49. // Consume a hierarchy defined by a list of integers. Each integer is an index
  50. // to its parent. The root node is its own parent, and it must be the first node
  51. // in the list. Clients do not have control over individual radiuses, only the
  52. // radius of the outermost enclosing disk.
  53. par_bubbles_t* par_bubbles_hpack_circle(int* nodes, int nnodes, double radius);
  54. // Queries ---------------------------------------------------------------------
  55. // Find the node at the given position. Children are on top of their parents.
  56. // If the result is -1, there is no node at the given pick coordinate.
  57. int par_bubbles_pick(par_bubbles_t const*, double x, double y);
  58. // Get bounding box; take a pointer to 4 floats and set them to min xy, max xy.
  59. void par_bubbles_compute_aabb(par_bubbles_t const*, double* aabb);
  60. // Check if the given circle (3-tuple) intersects the given aabb (4-tuple).
  61. bool par_bubbles_check_aabb(double const* disk, double const* aabb);
  62. // Clip the bubble diagram to the given AABB (4-tuple of left,bottom,right,top)
  63. // and return the result. Circles smaller than the given world-space
  64. // "minradius" are removed. Optionally, an existing diagram (dst) can be passed
  65. // in to receive the culled dataset, which reduces the number of memory allocs
  66. // when calling this function frequently. Pass null to "dst" to create a new
  67. // culled diagram.
  68. par_bubbles_t* par_bubbles_cull(par_bubbles_t const* src,
  69. double const* aabb, double minradius, par_bubbles_t* dst);
  70. // Dump out a SVG file for diagnostic purposes.
  71. void par_bubbles_export(par_bubbles_t const* bubbles, char const* filename);
  72. #ifndef PAR_HELPERS
  73. #define PAR_HELPERS 1
  74. #define PAR_PI (3.14159265359)
  75. #define PAR_MIN(a, b) (a > b ? b : a)
  76. #define PAR_MAX(a, b) (a > b ? a : b)
  77. #define PAR_CLAMP(v, lo, hi) PAR_MAX(lo, PAR_MIN(hi, v))
  78. #define PAR_MALLOC(T, N) ((T*) malloc(N * sizeof(T)))
  79. #define PAR_CALLOC(T, N) ((T*) calloc(N * sizeof(T), 1))
  80. #define PAR_REALLOC(T, BUF, N) ((T*) realloc(BUF, sizeof(T) * N))
  81. #define PAR_FREE(BUF) free(BUF)
  82. #define PAR_SWAP(T, A, B) { T tmp = B; B = A; A = tmp; }
  83. #define PAR_SQR(a) ((a) * (a))
  84. #endif
  85. // -----------------------------------------------------------------------------
  86. // END PUBLIC API
  87. // -----------------------------------------------------------------------------
  88. #endif // PAR_BUBBLES_H
  89. #ifdef PAR_BUBBLES_IMPLEMENTATION
  90. #include <math.h>
  91. #include <stdio.h>
  92. #include <stdlib.h>
  93. #include <float.h>
  94. #include <assert.h>
  95. typedef struct {
  96. int prev;
  97. int next;
  98. } par_bubbles__node;
  99. typedef struct {
  100. double* xyr; // results array
  101. int count; // client-provided count
  102. int* ids; // populated by par_bubbles_cull
  103. double const* radiuses; // client-provided radius list
  104. par_bubbles__node* chain; // counterclockwise enveloping chain
  105. int const* graph_parents; // client-provided parent indices
  106. int* graph_children; // flat list of children indices
  107. int* graph_heads; // list of "pointers" to first child
  108. int* graph_tails; // list of "pointers" to one-past-last child
  109. int npacked;
  110. int maxwidth;
  111. int capacity;
  112. } par_bubbles__t;
  113. static double par_bubbles__len2(double const* a)
  114. {
  115. return a[0] * a[0] + a[1] * a[1];
  116. }
  117. static void par_bubbles__initgraph(par_bubbles__t* bubbles)
  118. {
  119. int const* parents = bubbles->graph_parents;
  120. int* nchildren = PAR_CALLOC(int, bubbles->count);
  121. for (int i = 0; i < bubbles->count; i++) {
  122. nchildren[parents[i]]++;
  123. }
  124. int c = 0;
  125. bubbles->graph_heads = PAR_CALLOC(int, bubbles->count * 2);
  126. bubbles->graph_tails = bubbles->graph_heads + bubbles->count;
  127. for (int i = 0; i < bubbles->count; i++) {
  128. bubbles->maxwidth = PAR_MAX(bubbles->maxwidth, nchildren[i]);
  129. bubbles->graph_heads[i] = bubbles->graph_tails[i] = c;
  130. c += nchildren[i];
  131. }
  132. bubbles->graph_heads[0] = bubbles->graph_tails[0] = 1;
  133. bubbles->graph_children = PAR_MALLOC(int, c);
  134. for (int i = 1; i < bubbles->count; i++) {
  135. int parent = parents[i];
  136. bubbles->graph_children[bubbles->graph_tails[parent]++] = i;
  137. }
  138. PAR_FREE(nchildren);
  139. }
  140. static void par_bubbles__initflat(par_bubbles__t* bubbles)
  141. {
  142. double* xyr = bubbles->xyr;
  143. double const* radii = bubbles->radiuses;
  144. par_bubbles__node* chain = bubbles->chain;
  145. *xyr++ = -*radii;
  146. *xyr++ = 0;
  147. *xyr++ = *radii++;
  148. if (bubbles->count == ++bubbles->npacked) {
  149. return;
  150. }
  151. *xyr++ = *radii;
  152. *xyr++ = 0;
  153. *xyr++ = *radii++;
  154. if (bubbles->count == ++bubbles->npacked) {
  155. return;
  156. }
  157. xyr[2] = *radii;
  158. par_bubbles_touch_two_disks(xyr, xyr - 6, xyr - 3);
  159. if (bubbles->count == ++bubbles->npacked) {
  160. return;
  161. }
  162. chain[0].prev = 2;
  163. chain[0].next = 1;
  164. chain[1].prev = 0;
  165. chain[1].next = 2;
  166. chain[2].prev = 1;
  167. chain[2].next = 0;
  168. }
  169. // March forward or backward along the enveloping chain, starting with the
  170. // node at "cn" and testing for collision against the node at "ci".
  171. static int par_bubbles__collide(par_bubbles__t* bubbles, int ci, int cn,
  172. int* cj, int direction)
  173. {
  174. double const* ci_xyr = bubbles->xyr + ci * 3;
  175. par_bubbles__node* chain = bubbles->chain;
  176. int nsteps = 1;
  177. if (direction > 0) {
  178. for (int i = chain[cn].next; i != cn; i = chain[i].next, ++nsteps) {
  179. double const* i_xyr = bubbles->xyr + i * 3;
  180. double dx = i_xyr[0] - ci_xyr[0];
  181. double dy = i_xyr[1] - ci_xyr[1];
  182. double dr = i_xyr[2] + ci_xyr[2];
  183. if (0.999 * dr * dr > dx * dx + dy * dy) {
  184. *cj = i;
  185. return nsteps;
  186. }
  187. }
  188. return 0;
  189. }
  190. for (int i = chain[cn].prev; i != cn; i = chain[i].prev, ++nsteps) {
  191. double const* i_xyr = bubbles->xyr + i * 3;
  192. double dx = i_xyr[0] - ci_xyr[0];
  193. double dy = i_xyr[1] - ci_xyr[1];
  194. double dr = i_xyr[2] + ci_xyr[2];
  195. if (0.999 * dr * dr > dx * dx + dy * dy) {
  196. *cj = i;
  197. return nsteps;
  198. }
  199. }
  200. return 0;
  201. }
  202. static void par_bubbles__packflat(par_bubbles__t* bubbles)
  203. {
  204. double const* radii = bubbles->radiuses;
  205. double* xyr = bubbles->xyr;
  206. par_bubbles__node* chain = bubbles->chain;
  207. // Find the circle closest to the origin, known as "Cm" in the paper.
  208. int cm = 0;
  209. double mindist = par_bubbles__len2(xyr + 0);
  210. double dist = par_bubbles__len2(xyr + 3);
  211. if (dist > mindist) {
  212. cm = 1;
  213. }
  214. dist = par_bubbles__len2(xyr + 6);
  215. if (dist > mindist) {
  216. cm = 2;
  217. }
  218. // In the paper, "Cn" is always the node that follows "Cm".
  219. int ci, cn = chain[cm].next;
  220. for (ci = bubbles->npacked; ci < bubbles->count; ) {
  221. double* ci_xyr = xyr + ci * 3;
  222. ci_xyr[2] = radii[ci];
  223. double* cm_xyr = xyr + cm * 3;
  224. double* cn_xyr = xyr + cn * 3;
  225. par_bubbles_touch_two_disks(ci_xyr, cn_xyr, cm_xyr);
  226. // Check for a collision. In the paper, "Cj" is the intersecting node.
  227. int cj_f;
  228. int nfsteps = par_bubbles__collide(bubbles, ci, cn, &cj_f, +1);
  229. if (!nfsteps) {
  230. chain[cm].next = ci;
  231. chain[ci].prev = cm;
  232. chain[ci].next = cn;
  233. chain[cn].prev = ci;
  234. cm = ci++;
  235. continue;
  236. }
  237. // Search backwards for a collision, in case it is closer.
  238. int cj_b;
  239. int nbsteps = par_bubbles__collide(bubbles, ci, cm, &cj_b, -1);
  240. // Intersection occurred after Cn.
  241. if (nfsteps <= nbsteps) {
  242. cn = cj_f;
  243. chain[cm].next = cn;
  244. chain[cn].prev = cm;
  245. continue;
  246. }
  247. // Intersection occurred before Cm.
  248. cm = cj_b;
  249. chain[cm].next = cn;
  250. chain[cn].prev = cm;
  251. }
  252. bubbles->npacked = bubbles->count;
  253. }
  254. static void par__disk_from_two(double const* xy1, double const* xy2,
  255. double* result)
  256. {
  257. double dx = xy1[0] - xy2[0];
  258. double dy = xy1[1] - xy2[1];
  259. result[0] = 0.5 * (xy1[0] + xy2[0]);
  260. result[1] = 0.5 * (xy1[1] + xy2[1]);
  261. result[2] = sqrt(dx * dx + dy * dy) / 2.0;
  262. }
  263. static int par__disk_contains(double const* xyr, double const* xy)
  264. {
  265. double dx = xyr[0] - xy[0];
  266. double dy = xyr[1] - xy[1];
  267. return dx * dx + dy * dy <= PAR_SQR(xyr[2]);
  268. }
  269. static void par__easydisk(double* disk, double const* edgepts, int nedgepts)
  270. {
  271. if (nedgepts == 0) {
  272. disk[0] = 0;
  273. disk[1] = 0;
  274. disk[2] = 0;
  275. return;
  276. }
  277. if (nedgepts == 1) {
  278. disk[0] = edgepts[0];
  279. disk[1] = edgepts[1];
  280. disk[2] = 0;
  281. return;
  282. }
  283. par__disk_from_two(edgepts, edgepts + 2, disk);
  284. if (nedgepts == 2 || par__disk_contains(disk, edgepts + 4)) {
  285. return;
  286. }
  287. par__disk_from_two(edgepts, edgepts + 4, disk);
  288. if (par__disk_contains(disk, edgepts + 2)) {
  289. return;
  290. }
  291. par__disk_from_two(edgepts + 2, edgepts + 4, disk);
  292. if (par__disk_contains(disk, edgepts)) {
  293. return;
  294. }
  295. par_bubbles_touch_three_points(edgepts, disk);
  296. }
  297. static void par__minidisk(double* disk, double const* pts, int npts,
  298. double const* edgepts, int nedgepts)
  299. {
  300. if (npts == 0 || nedgepts == 3) {
  301. par__easydisk(disk, edgepts, nedgepts);
  302. return;
  303. }
  304. assert(nedgepts < 3);
  305. double const* pt = pts + (--npts) * 2;
  306. par__minidisk(disk, pts, npts, edgepts, nedgepts);
  307. if (!par__disk_contains(disk, pt)) {
  308. double edgepts1[6];
  309. for (int i = 0; i < nedgepts * 2; i += 2) {
  310. edgepts1[i] = edgepts[i];
  311. edgepts1[i + 1] = edgepts[i + 1];
  312. }
  313. edgepts1[2 * nedgepts] = pt[0];
  314. edgepts1[2 * nedgepts + 1] = pt[1];
  315. par__minidisk(disk, pts, npts, edgepts1, ++nedgepts);
  316. }
  317. }
  318. static void par_bubbles__copy_disk(par_bubbles__t const* src,
  319. par_bubbles__t* dst, int parent)
  320. {
  321. int i = dst->count++;
  322. if (dst->capacity < dst->count) {
  323. dst->capacity = PAR_MAX(16, dst->capacity) * 2;
  324. dst->xyr = PAR_REALLOC(double, dst->xyr, 3 * dst->capacity);
  325. dst->ids = PAR_REALLOC(int, dst->ids, dst->capacity);
  326. }
  327. double const* xyr = src->xyr + parent * 3;
  328. dst->xyr[i * 3] = xyr[0];
  329. dst->xyr[i * 3 + 1] = xyr[1];
  330. dst->xyr[i * 3 + 2] = xyr[2];
  331. dst->ids[i] = parent;
  332. }
  333. void par_bubbles_enclose_points(double const* xy, int npts, double* result)
  334. {
  335. if (npts == 0) {
  336. return;
  337. }
  338. par__minidisk(result, xy, npts, 0, 0);
  339. }
  340. void par_bubbles_enclose_disks(double const* xyr, int ndisks, double* result)
  341. {
  342. int ngon = 8;
  343. int npts = ndisks * ngon;
  344. double* pts = PAR_MALLOC(double, npts * 2);
  345. double* ppts = pts;
  346. float dtheta = PAR_PI * 2.0 / ngon;
  347. for (int i = 0; i < ndisks; i++) {
  348. double cx = xyr[i * 3];
  349. double cy = xyr[i * 3 + 1];
  350. double cr = xyr[i * 3 + 2];
  351. double a = 2.0 * cr / (1.0 + sqrt(2));
  352. double r = 0.5 * sqrt(2) * a * sqrt(2 + sqrt(2));
  353. float theta = 0;
  354. for (int j = 0; j < ngon; j++, theta += dtheta) {
  355. *ppts++ = cx + r * cos(theta);
  356. *ppts++ = cy + r * sin(theta);
  357. }
  358. }
  359. par_bubbles_enclose_points(pts, npts, result);
  360. PAR_FREE(pts);
  361. }
  362. void par_bubbles_touch_three_points(double const* xy, double* xyr)
  363. {
  364. // Many thanks to Stephen Schmitts:
  365. // http://www.abecedarical.com/zenosamples/zs_circle3pts.html
  366. double p1x = xy[0], p1y = xy[1];
  367. double p2x = xy[2], p2y = xy[3];
  368. double p3x = xy[4], p3y = xy[5];
  369. double a = p2x - p1x, b = p2y - p1y;
  370. double c = p3x - p1x, d = p3y - p1y;
  371. double e = a * (p2x + p1x) * 0.5 + b * (p2y + p1y) * 0.5;
  372. double f = c * (p3x + p1x) * 0.5 + d * (p3y + p1y) * 0.5;
  373. double det = a*d - b*c;
  374. double cx = xyr[0] = (d*e - b*f) / det;
  375. double cy = xyr[1] = (-c*e + a*f) / det;
  376. xyr[2] = sqrt((p1x - cx)*(p1x - cx) + (p1y - cy)*(p1y - cy));
  377. }
  378. void par_bubbles_touch_two_disks(double* c, double const* a, double const* b)
  379. {
  380. double db = a[2] + c[2], dx = b[0] - a[0], dy = b[1] - a[1];
  381. if (db && (dx || dy)) {
  382. double da = b[2] + c[2], dc = dx * dx + dy * dy;
  383. da *= da;
  384. db *= db;
  385. double x = 0.5 + (db - da) / (2 * dc);
  386. double db1 = db - dc;
  387. double y0 = PAR_MAX(0, 2 * da * (db + dc) - db1 * db1 - da * da);
  388. double y = sqrt(y0) / (2 * dc);
  389. c[0] = a[0] + x * dx + y * dy;
  390. c[1] = a[1] + x * dy - y * dx;
  391. } else {
  392. c[0] = a[0] + db;
  393. c[1] = a[1];
  394. }
  395. }
  396. void par_bubbles_free_result(par_bubbles_t* pubbub)
  397. {
  398. par_bubbles__t* bubbles = (par_bubbles__t*) pubbub;
  399. PAR_FREE(bubbles->graph_children);
  400. PAR_FREE(bubbles->graph_heads);
  401. PAR_FREE(bubbles->chain);
  402. PAR_FREE(bubbles->xyr);
  403. PAR_FREE(bubbles->ids);
  404. PAR_FREE(bubbles);
  405. }
  406. par_bubbles_t* par_bubbles_pack(double const* radiuses, int nradiuses)
  407. {
  408. par_bubbles__t* bubbles = PAR_CALLOC(par_bubbles__t, 1);
  409. if (nradiuses > 0) {
  410. bubbles->radiuses = radiuses;
  411. bubbles->count = nradiuses;
  412. bubbles->chain = PAR_MALLOC(par_bubbles__node, nradiuses);
  413. bubbles->xyr = PAR_MALLOC(double, 3 * nradiuses);
  414. par_bubbles__initflat(bubbles);
  415. par_bubbles__packflat(bubbles);
  416. }
  417. return (par_bubbles_t*) bubbles;
  418. }
  419. // TODO: use a stack instead of recursion
  420. void par_bubbles__compute_radius(par_bubbles__t* bubbles,
  421. par_bubbles__t* worker, int parent)
  422. {
  423. int head = bubbles->graph_heads[parent];
  424. int tail = bubbles->graph_tails[parent];
  425. int nchildren = tail - head;
  426. int pr = parent * 3 + 2;
  427. bubbles->xyr[pr] = 1;
  428. if (nchildren == 0) {
  429. return;
  430. }
  431. for (int children_index = head; children_index != tail; children_index++) {
  432. int child = bubbles->graph_children[children_index];
  433. par_bubbles__compute_radius(bubbles, worker, child);
  434. bubbles->xyr[pr] += bubbles->xyr[child * 3 + 2];
  435. }
  436. bubbles->xyr[pr] = sqrtf(bubbles->xyr[pr]);
  437. }
  438. void par_bubbles__hpack(par_bubbles__t* bubbles, par_bubbles__t* worker,
  439. int parent)
  440. {
  441. int head = bubbles->graph_heads[parent];
  442. int tail = bubbles->graph_tails[parent];
  443. int nchildren = tail - head;
  444. if (nchildren == 0) {
  445. return;
  446. }
  447. // Cast away const because we're using the worker as a cache to avoid
  448. // a kazillion malloc / free calls.
  449. double* radiuses = (double*) worker->radiuses;
  450. // We perform flat layout twice: once without padding (to determine scale)
  451. // and then again with scaled padding.
  452. double enclosure[3];
  453. double px = bubbles->xyr[parent * 3 + 0];
  454. double py = bubbles->xyr[parent * 3 + 1];
  455. double pr = bubbles->xyr[parent * 3 + 2];
  456. const double PAR_HPACK_PADDING1 = 0.1;
  457. const double PAR_HPACK_PADDING2 = 0.05;
  458. double scaled_padding = 0.0;
  459. while (1) {
  460. worker->npacked = 0;
  461. worker->count = nchildren;
  462. int c = 0;
  463. for (int cindex = head; cindex != tail; cindex++) {
  464. int child = bubbles->graph_children[cindex];
  465. radiuses[c++] = bubbles->xyr[child * 3 + 2] + scaled_padding;
  466. }
  467. par_bubbles__initflat(worker);
  468. par_bubbles__packflat(worker);
  469. // Using Welzl's algorithm instead of a simple AABB enclosure is
  470. // slightly slower and doesn't yield much aesthetic improvement.
  471. #if PAR_BUBBLES_HPACK_WELZL
  472. par_bubbles_enclose_disks(worker->xyr, nchildren, enclosure);
  473. #else
  474. double aabb[6];
  475. par_bubbles_compute_aabb((par_bubbles_t const*) worker, aabb);
  476. enclosure[0] = 0.5 * (aabb[0] + aabb[2]);
  477. enclosure[1] = 0.5 * (aabb[1] + aabb[3]);
  478. enclosure[2] = 0;
  479. for (int c = 0; c < nchildren; c++) {
  480. double x = worker->xyr[c * 3 + 0] - enclosure[0];
  481. double y = worker->xyr[c * 3 + 1] - enclosure[1];
  482. double r = worker->xyr[c * 3 + 2];
  483. enclosure[2] = PAR_MAX(enclosure[2], r + sqrtf(x * x + y * y));
  484. }
  485. #endif
  486. if (scaled_padding || !PAR_HPACK_PADDING1) {
  487. break;
  488. } else {
  489. scaled_padding = PAR_HPACK_PADDING1 / enclosure[2];
  490. }
  491. }
  492. double cx = enclosure[0], cy = enclosure[1], cr = enclosure[2];
  493. scaled_padding *= cr;
  494. cr += PAR_HPACK_PADDING2 * cr;
  495. if (nchildren == 1) {
  496. cr *= 2;
  497. }
  498. // Transform the children to fit nicely into their parent.
  499. int c = 0;
  500. pr /= cr;
  501. for (int cindex = head; cindex != tail; cindex++) {
  502. int child = bubbles->graph_children[cindex];
  503. bubbles->xyr[child * 3 + 0] = px + pr * (worker->xyr[c * 3 + 0] - cx);
  504. bubbles->xyr[child * 3 + 1] = py + pr * (worker->xyr[c * 3 + 1] - cy);
  505. bubbles->xyr[child * 3 + 2] -= scaled_padding;
  506. bubbles->xyr[child * 3 + 2] *= pr;
  507. c++;
  508. }
  509. // Recursion. TODO: It might be better to use our own stack here.
  510. for (int cindex = head; cindex != tail; cindex++) {
  511. par_bubbles__hpack(bubbles, worker, bubbles->graph_children[cindex]);
  512. }
  513. }
  514. par_bubbles_t* par_bubbles_hpack_circle(int* nodes, int nnodes, double radius)
  515. {
  516. par_bubbles__t* bubbles = PAR_CALLOC(par_bubbles__t, 1);
  517. if (nnodes > 0) {
  518. bubbles->graph_parents = nodes;
  519. bubbles->count = nnodes;
  520. bubbles->chain = PAR_MALLOC(par_bubbles__node, nnodes);
  521. bubbles->xyr = PAR_MALLOC(double, 3 * nnodes);
  522. par_bubbles__initgraph(bubbles);
  523. par_bubbles__t* worker = PAR_CALLOC(par_bubbles__t, 1);
  524. worker->radiuses = PAR_MALLOC(double, bubbles->maxwidth);
  525. worker->chain = PAR_MALLOC(par_bubbles__node, bubbles->maxwidth);
  526. worker->xyr = PAR_MALLOC(double, 3 * bubbles->maxwidth);
  527. par_bubbles__compute_radius(bubbles, worker, 0);
  528. bubbles->xyr[0] = 0;
  529. bubbles->xyr[1] = 0;
  530. bubbles->xyr[2] = radius;
  531. par_bubbles__hpack(bubbles, worker, 0);
  532. par_bubbles_free_result((par_bubbles_t*) worker);
  533. }
  534. return (par_bubbles_t*) bubbles;
  535. }
  536. // TODO: use a stack instead of recursion
  537. static int par_bubbles__pick(par_bubbles__t const* bubbles, int parent,
  538. double x, double y)
  539. {
  540. double const* xyr = bubbles->xyr + parent * 3;
  541. double d2 = PAR_SQR(x - xyr[0]) + PAR_SQR(y - xyr[1]);
  542. if (d2 > PAR_SQR(xyr[2])) {
  543. return -1;
  544. }
  545. int head = bubbles->graph_heads[parent];
  546. int tail = bubbles->graph_tails[parent];
  547. for (int cindex = head; cindex != tail; cindex++) {
  548. int child = bubbles->graph_children[cindex];
  549. int result = par_bubbles__pick(bubbles, child, x, y);
  550. if (result > -1) {
  551. return result;
  552. }
  553. }
  554. return parent;
  555. }
  556. int par_bubbles_pick(par_bubbles_t const* cbubbles, double x, double y)
  557. {
  558. par_bubbles__t const* bubbles = (par_bubbles__t const*) cbubbles;
  559. if (bubbles->count == 0) {
  560. return -1;
  561. }
  562. return par_bubbles__pick(bubbles, 0, x, y);
  563. }
  564. void par_bubbles_compute_aabb(par_bubbles_t const* bubbles, double* aabb)
  565. {
  566. if (bubbles->count == 0) {
  567. return;
  568. }
  569. double* xyr = bubbles->xyr;
  570. aabb[0] = aabb[2] = xyr[0];
  571. aabb[1] = aabb[3] = xyr[1];
  572. for (int i = 0; i < bubbles->count; i++, xyr += 3) {
  573. aabb[0] = PAR_MIN(xyr[0] - xyr[2], aabb[0]);
  574. aabb[1] = PAR_MIN(xyr[1] - xyr[2], aabb[1]);
  575. aabb[2] = PAR_MAX(xyr[0] + xyr[2], aabb[2]);
  576. aabb[3] = PAR_MAX(xyr[1] + xyr[2], aabb[3]);
  577. }
  578. }
  579. bool par_bubbles_check_aabb(double const* disk, double const* aabb)
  580. {
  581. double cx = PAR_CLAMP(disk[0], aabb[0], aabb[2]);
  582. double cy = PAR_CLAMP(disk[1], aabb[1], aabb[3]);
  583. double dx = disk[0] - cx;
  584. double dy = disk[1] - cy;
  585. double d2 = dx * dx + dy * dy;
  586. return d2 < (disk[2] * disk[2]);
  587. }
  588. static void par_bubbles__cull(par_bubbles__t const* src, double const* aabb,
  589. double minradius, par_bubbles__t* dst, int parent)
  590. {
  591. double const* xyr = src->xyr + parent * 3;
  592. if (xyr[2] < minradius || !par_bubbles_check_aabb(xyr, aabb)) {
  593. return;
  594. }
  595. par_bubbles__copy_disk(src, dst, parent);
  596. int head = src->graph_heads[parent];
  597. int tail = src->graph_tails[parent];
  598. for (int cindex = head; cindex != tail; cindex++) {
  599. int child = src->graph_children[cindex];
  600. par_bubbles__cull(src, aabb, minradius, dst, child);
  601. }
  602. }
  603. par_bubbles_t* par_bubbles_cull(par_bubbles_t const* psrc,
  604. double const* aabb, double minradius, par_bubbles_t* pdst)
  605. {
  606. par_bubbles__t const* src = (par_bubbles__t const*) psrc;
  607. par_bubbles__t* dst = (par_bubbles__t*) pdst;
  608. if (!dst) {
  609. dst = PAR_CALLOC(par_bubbles__t, 1);
  610. pdst = (par_bubbles_t*) dst;
  611. } else {
  612. dst->count = 0;
  613. }
  614. if (src->count == 0) {
  615. return pdst;
  616. }
  617. par_bubbles__cull(src, aabb, minradius, dst, 0);
  618. return pdst;
  619. }
  620. void par_bubbles_export(par_bubbles_t const* bubbles, char const* filename)
  621. {
  622. double aabb[4];
  623. par_bubbles_compute_aabb(bubbles, aabb);
  624. double maxextent = PAR_MAX(aabb[2] - aabb[0], aabb[3] - aabb[1]);
  625. double padding = 0.05 * maxextent;
  626. FILE* svgfile = fopen(filename, "wt");
  627. fprintf(svgfile,
  628. "<svg viewBox='%f %f %f %f' width='700px' height='700px' "
  629. "version='1.1' "
  630. "xmlns='http://www.w3.org/2000/svg'>\n"
  631. "<g stroke-width='0.5' stroke-opacity='0.5' stroke='black' "
  632. "fill-opacity='0.2' fill='#2A8BB6' "
  633. "transform='scale(1 -1) transform(0 %f)'>\n"
  634. "<rect fill-opacity='0.1' stroke='none' fill='#2A8BB6' x='%f' y='%f' "
  635. "width='100%%' height='100%%'/>\n",
  636. aabb[0] - padding, aabb[1] - padding,
  637. aabb[2] - aabb[0] + 2 * padding, aabb[3] - aabb[1] + 2 * padding,
  638. aabb[1] - padding,
  639. aabb[0] - padding, aabb[1] - padding);
  640. double const* xyr = bubbles->xyr;
  641. for (int i = 0; i < bubbles->count; i++, xyr += 3) {
  642. fprintf(svgfile, "<circle stroke-width='%f' cx='%f' cy='%f' r='%f'/>\n",
  643. xyr[2] * 0.01, xyr[0], xyr[1], xyr[2]);
  644. fprintf(svgfile, "<text text-anchor='middle' stroke='none' "
  645. "x='%f' y='%f' font-size='%f'>%d</text>\n",
  646. xyr[0], xyr[1] + xyr[2] * 0.125, xyr[2] * 0.5, i);
  647. }
  648. fputs("</g>\n</svg>", svgfile);
  649. fclose(svgfile);
  650. }
  651. #endif // PAR_BUBBLES_IMPLEMENTATION