stb_connected_components.h 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. // stb_connected_components - v0.91 - public domain connected components on grids
  2. // http://github.com/nothings/stb
  3. //
  4. // Finds connected components on 2D grids for testing reachability between
  5. // two points, with fast updates when changing reachability (e.g. on one machine
  6. // it was typically 0.2ms w/ 1024x1024 grid). Each grid square must be "open" or
  7. // "closed" (traversable or untraversable), and grid squares are only connected
  8. // to their orthogonal neighbors, not diagonally.
  9. //
  10. // In one source file, create the implementation by doing something like this:
  11. //
  12. // #define STBCC_GRID_COUNT_X_LOG2 10
  13. // #define STBCC_GRID_COUNT_Y_LOG2 10
  14. // #define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
  15. // #include "stb_connected_components.h"
  16. //
  17. // The above creates an implementation that can run on maps up to 1024x1024.
  18. // Map sizes must be a multiple of 32 on each axis.
  19. //
  20. // LICENSE
  21. //
  22. // This software is dual-licensed to the public domain and under the following
  23. // license: you are granted a perpetual, irrevocable license to copy, modify,
  24. // publish, and distribute this file as you see fit.
  25. //
  26. //
  27. // CHANGELOG
  28. //
  29. // 0.92 (2016-04-16) Compute sqrt(N) cluster size by default
  30. // 0.91 (2016-04-15) Initial release
  31. //
  32. // TODO:
  33. // - better API documentation
  34. // - more comments
  35. // - try re-integrating naive algorithm & compare performance
  36. // - more optimized batching (current approach still recomputes clumps many times)
  37. // - function for setting a grid of squares at once (just use batching)
  38. // - shrink data by storing only, say, 2X max exits
  39. // (instead of max exits per clump), and repack cluster
  40. // if it runs out (possibly by just rebuilding from scratch,
  41. // could even use dirty-cluster data structure)
  42. // should reduce 1Kx1K from ~66MB to ~8MB
  43. //
  44. // ALGORITHM
  45. //
  46. // The NxN grid map is split into sqrt(N) x sqrt(N) blocks called
  47. // "clusters". Each cluster independently computes a set of connected
  48. // components within that cluster (ignoring all connectivity out of
  49. // that cluster) using a union-find disjoint set forest. This produces a bunch
  50. // of locally connected components called "clumps". Each clump is (a) connected
  51. // within its cluster, (b) does not directly connect to any other clumps in the
  52. // cluster (though it may connect to them by paths that lead outside the cluster,
  53. // but those are ignored at this step), and (c) maintains an adjacency list of
  54. // all clumps in adjacent clusters that it _is_ connected to. Then a second
  55. // union-find disjoint set forest is used to compute connected clumps
  56. // globally, across the whole map. Reachability is then computed by
  57. // finding which clump each input point belongs to, and checking whether
  58. // those clumps are in the same "global" connected component.
  59. //
  60. // The above data structure can be updated efficiently; on a change
  61. // of a single grid square on the map, only one cluster changes its
  62. // purely-local state, so only one cluster needs its clumps fully
  63. // recomputed. Clumps in adjacent clusters need their adjacency lists
  64. // updated: first to remove all references to the old clumps in the
  65. // rebuilt cluster, then to add new references to the new clumps. Both
  66. // of these operations can use the existing "find which clump each input
  67. // point belongs to" query to compute that adjacency information rapidly.
  68. // In one 1024x1024 test on a specific machine, a one-tile update was
  69. // about 250 times faster than a full disjoint-set-forest on the full map.
  70. #ifndef INCLUDE_STB_CONNECTED_COMPONENTS_H
  71. #define INCLUDE_STB_CONNECTED_COMPONENTS_H
  72. #include <stdlib.h>
  73. #include <assert.h>
  74. typedef struct st_stbcc_grid stbcc_grid;
  75. #ifdef __cplusplus
  76. extern "C" {
  77. #endif
  78. //////////////////////////////////////////////////////////////////////////////////////////
  79. //
  80. // initialization
  81. //
  82. // you allocate the grid data structure to this size (note that it will be very big!!!)
  83. extern size_t stbcc_grid_sizeof(void);
  84. // initialize the grid, value of map[] is 0 = traversable, non-0 is solid
  85. extern void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h);
  86. //////////////////////////////////////////////////////////////////////////////////////////
  87. //
  88. // main functionality
  89. //
  90. // update a grid square state, 0 = traversable, non-0 is solid
  91. // i can add a batch-update if it's needed
  92. extern void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid);
  93. // query if two grid squares are reachable from each other
  94. extern int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2);
  95. //////////////////////////////////////////////////////////////////////////////////////////
  96. //
  97. // bonus functions
  98. //
  99. // wrap multiple stbcc_update_grid calls in these function to compute
  100. // multiple updates more efficiently; cannot make queries inside batch
  101. extern void stbcc_update_batch_begin(stbcc_grid *g);
  102. extern void stbcc_update_batch_end(stbcc_grid *g);
  103. // query the grid data structure for whether a given square is open or not
  104. extern int stbcc_query_grid_open(stbcc_grid *g, int x, int y);
  105. // get a unique id for the connected component this is in; it's not necessarily
  106. // small, you'll need a hash table or something to remap it (or just use
  107. extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
  108. #define STBCC_NULL_UNIQUE_ID 0xffffffff // returned for closed map squares
  109. #ifdef __cplusplus
  110. }
  111. #endif
  112. #endif // INCLUDE_STB_CONNECTED_COMPONENTS_H
  113. #ifdef STB_CONNECTED_COMPONENTS_IMPLEMENTATION
  114. #if !defined(STBCC_GRID_COUNT_X_LOG2) || !defined(STBCC_GRID_COUNT_Y_LOG2)
  115. #error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
  116. #endif
  117. #define STBCC__GRID_COUNT_X (1 << STBCC_GRID_COUNT_X_LOG2)
  118. #define STBCC__GRID_COUNT_Y (1 << STBCC_GRID_COUNT_Y_LOG2)
  119. #define STBCC__MAP_STRIDE (1 << (STBCC_GRID_COUNT_X_LOG2-3))
  120. #ifndef STBCC_CLUSTER_SIZE_X_LOG2
  121. #define STBCC_CLUSTER_SIZE_X_LOG2 (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
  122. #if STBCC_CLUSTER_SIZE_X_LOG2 > 6
  123. #undef STBCC_CLUSTER_SIZE_X_LOG2
  124. #define STBCC_CLUSTER_SIZE_X_LOG2 6
  125. #endif
  126. #endif
  127. #ifndef STBCC_CLUSTER_SIZE_Y_LOG2
  128. #define STBCC_CLUSTER_SIZE_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2/2)
  129. #if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
  130. #undef STBCC_CLUSTER_SIZE_Y_LOG2
  131. #define STBCC_CLUSTER_SIZE_Y_LOG2 6
  132. #endif
  133. #endif
  134. #define STBCC__CLUSTER_SIZE_X (1 << STBCC_CLUSTER_SIZE_X_LOG2)
  135. #define STBCC__CLUSTER_SIZE_Y (1 << STBCC_CLUSTER_SIZE_Y_LOG2)
  136. #define STBCC__CLUSTER_COUNT_X_LOG2 (STBCC_GRID_COUNT_X_LOG2 - STBCC_CLUSTER_SIZE_X_LOG2)
  137. #define STBCC__CLUSTER_COUNT_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2 - STBCC_CLUSTER_SIZE_Y_LOG2)
  138. #define STBCC__CLUSTER_COUNT_X (1 << STBCC__CLUSTER_COUNT_X_LOG2)
  139. #define STBCC__CLUSTER_COUNT_Y (1 << STBCC__CLUSTER_COUNT_Y_LOG2)
  140. #if STBCC__CLUSTER_SIZE_X >= STBCC__GRID_COUNT_X || STBCC__CLUSTER_SIZE_Y >= STBCC__GRID_COUNT_Y
  141. #error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
  142. #endif
  143. // worst case # of clumps per cluster
  144. #define STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2 (STBCC_CLUSTER_SIZE_X_LOG2 + STBCC_CLUSTER_SIZE_Y_LOG2-1)
  145. #define STBCC__MAX_CLUMPS_PER_CLUSTER (1 << STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2)
  146. #define STBCC__MAX_CLUMPS (STBCC__MAX_CLUMPS_PER_CLUSTER * STBCC__CLUSTER_COUNT_X * STBCC__CLUSTER_COUNT_Y)
  147. #define STBCC__NULL_CLUMPID STBCC__MAX_CLUMPS_PER_CLUSTER
  148. #define STBCC__CLUSTER_X_FOR_COORD_X(x) ((x) >> STBCC_CLUSTER_SIZE_X_LOG2)
  149. #define STBCC__CLUSTER_Y_FOR_COORD_Y(y) ((y) >> STBCC_CLUSTER_SIZE_Y_LOG2)
  150. #define STBCC__MAP_BYTE_MASK(x,y) (1 << ((x) & 7))
  151. #define STBCC__MAP_BYTE(g,x,y) ((g)->map[y][(x) >> 3])
  152. #define STBCC__MAP_OPEN(g,x,y) (STBCC__MAP_BYTE(g,x,y) & STBCC__MAP_BYTE_MASK(x,y))
  153. typedef unsigned short stbcc__clumpid;
  154. typedef unsigned char stbcc__verify_max_clumps[STBCC__MAX_CLUMPS_PER_CLUSTER < (1 << (8*sizeof(stbcc__clumpid))) ? 1 : -1];
  155. #define STBCC__MAX_EXITS_PER_CLUMP (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y) // 64
  156. // 2^19 * 2^6 => 2^25 exits => 2^26 => 64MB for 1024x1024
  157. typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256];
  158. typedef struct
  159. {
  160. unsigned short clump_index:12;
  161. signed short cluster_dx:2;
  162. signed short cluster_dy:2;
  163. } stbcc__relative_clumpid;
  164. typedef union
  165. {
  166. struct {
  167. unsigned int clump_index:12;
  168. unsigned int cluster_x:10;
  169. unsigned int cluster_y:10;
  170. } f;
  171. unsigned int c;
  172. } stbcc__global_clumpid;
  173. // rebuilt cluster 3,4
  174. // what changes in cluster 2,4
  175. typedef struct
  176. {
  177. stbcc__global_clumpid global_label;
  178. unsigned short num_adjacent;
  179. stbcc__relative_clumpid adjacent_clumps[STBCC__MAX_EXITS_PER_CLUMP];
  180. } stbcc__clump;
  181. typedef struct
  182. {
  183. unsigned int num_clumps;
  184. stbcc__clump clump[STBCC__MAX_CLUMPS_PER_CLUSTER];
  185. } stbcc__cluster;
  186. struct st_stbcc_grid
  187. {
  188. int w,h,cw,ch;
  189. int in_batched_update;
  190. //unsigned char cluster_dirty[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // could bitpack, but: 1K x 1K => 1KB
  191. unsigned char map[STBCC__GRID_COUNT_Y][STBCC__MAP_STRIDE]; // 1K x 1K => 1K x 128 => 128KB
  192. stbcc__clumpid clump_for_node[STBCC__GRID_COUNT_Y][STBCC__GRID_COUNT_X]; // 1K x 1K x 2 = 2MB
  193. stbcc__cluster cluster[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // 1K x 1K x 0.5 x 64 x 2 = 64MB
  194. };
  195. int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
  196. {
  197. stbcc__global_clumpid label1, label2;
  198. stbcc__clumpid c1 = g->clump_for_node[y1][x1];
  199. stbcc__clumpid c2 = g->clump_for_node[y2][x2];
  200. int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
  201. int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
  202. int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
  203. int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
  204. assert(!g->in_batched_update);
  205. if (c1 == STBCC__NULL_CLUMPID || c2 == STBCC__NULL_CLUMPID)
  206. return 0;
  207. label1 = g->cluster[cy1][cx1].clump[c1].global_label;
  208. label2 = g->cluster[cy2][cx2].clump[c2].global_label;
  209. if (label1.c == label2.c)
  210. return 1;
  211. return 0;
  212. }
  213. int stbcc_query_grid_open(stbcc_grid *g, int x, int y)
  214. {
  215. return STBCC__MAP_OPEN(g, x, y) != 0;
  216. }
  217. unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y)
  218. {
  219. stbcc__clumpid c = g->clump_for_node[y][x];
  220. int cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
  221. int cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y);
  222. assert(!g->in_batched_update);
  223. if (c == STBCC__NULL_CLUMPID) return STBCC_NULL_UNIQUE_ID;
  224. return g->cluster[cy][cx].clump[c].global_label.c;
  225. }
  226. typedef struct
  227. {
  228. unsigned char x,y;
  229. } stbcc__tinypoint;
  230. typedef struct
  231. {
  232. stbcc__tinypoint parent[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X]; // 32x32 => 2KB
  233. stbcc__clumpid label[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X];
  234. } stbcc__cluster_build_info;
  235. static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy);
  236. static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy);
  237. static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy);
  238. static stbcc__global_clumpid stbcc__clump_find(stbcc_grid *g, stbcc__global_clumpid n)
  239. {
  240. stbcc__global_clumpid q;
  241. stbcc__clump *c = &g->cluster[n.f.cluster_y][n.f.cluster_x].clump[n.f.clump_index];
  242. if (c->global_label.c == n.c)
  243. return n;
  244. q = stbcc__clump_find(g, c->global_label);
  245. c->global_label = q;
  246. return q;
  247. }
  248. typedef struct
  249. {
  250. unsigned int cluster_x;
  251. unsigned int cluster_y;
  252. unsigned int clump_index;
  253. } stbcc__unpacked_clumpid;
  254. // @OPTIMIZE: pass in these parameters unpacked, not packed
  255. static void stbcc__clump_union(stbcc_grid *g, stbcc__unpacked_clumpid m, int x, int y, int idx)
  256. {
  257. stbcc__clump *mc = &g->cluster[m.cluster_y][m.cluster_x].clump[m.clump_index];
  258. stbcc__clump *nc = &g->cluster[y][x].clump[idx];
  259. stbcc__global_clumpid mp = stbcc__clump_find(g, mc->global_label);
  260. stbcc__global_clumpid np = stbcc__clump_find(g, nc->global_label);
  261. if (mp.c == np.c)
  262. return;
  263. g->cluster[mp.f.cluster_y][mp.f.cluster_x].clump[mp.f.clump_index].global_label = np;
  264. }
  265. static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
  266. {
  267. int i,j,k,h;
  268. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
  269. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
  270. stbcc__cluster *cluster = &g->cluster[j][i];
  271. for (k=0; k < (int) cluster->num_clumps; ++k) {
  272. stbcc__global_clumpid m;
  273. m.f.clump_index = k;
  274. m.f.cluster_x = i;
  275. m.f.cluster_y = j;
  276. assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
  277. cluster->clump[k].global_label = m;
  278. }
  279. }
  280. }
  281. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
  282. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
  283. stbcc__cluster *cluster = &g->cluster[j][i];
  284. for (k=0; k < (int) cluster->num_clumps; ++k) {
  285. stbcc__clump *clump = &cluster->clump[k];
  286. stbcc__unpacked_clumpid m;
  287. m.clump_index = k;
  288. m.cluster_x = i;
  289. m.cluster_y = j;
  290. for (h=0; h < clump->num_adjacent; ++h) {
  291. unsigned int clump_index = clump->adjacent_clumps[h].clump_index;
  292. unsigned int x = clump->adjacent_clumps[h].cluster_dx + i;
  293. unsigned int y = clump->adjacent_clumps[h].cluster_dy + j;
  294. stbcc__clump_union(g, m, x, y, clump_index);
  295. }
  296. }
  297. }
  298. }
  299. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
  300. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
  301. stbcc__cluster *cluster = &g->cluster[j][i];
  302. for (k=0; k < (int) cluster->num_clumps; ++k) {
  303. stbcc__global_clumpid m;
  304. m.f.clump_index = k;
  305. m.f.cluster_x = i;
  306. m.f.cluster_y = j;
  307. stbcc__clump_find(g, m);
  308. }
  309. }
  310. }
  311. }
  312. void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
  313. {
  314. int cx,cy;
  315. if (!solid) {
  316. if (STBCC__MAP_OPEN(g,x,y))
  317. return;
  318. } else {
  319. if (!STBCC__MAP_OPEN(g,x,y))
  320. return;
  321. }
  322. cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
  323. cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y);
  324. stbcc__remove_connections_to_adjacent_cluster(g, cx-1, cy, 1, 0);
  325. stbcc__remove_connections_to_adjacent_cluster(g, cx+1, cy, -1, 0);
  326. stbcc__remove_connections_to_adjacent_cluster(g, cx, cy-1, 0, 1);
  327. stbcc__remove_connections_to_adjacent_cluster(g, cx, cy+1, 0,-1);
  328. if (!solid)
  329. STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
  330. else
  331. STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
  332. stbcc__build_clumps_for_cluster(g, cx, cy);
  333. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, -1, 0);
  334. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 1, 0);
  335. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0,-1);
  336. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0, 1);
  337. stbcc__add_connections_to_adjacent_cluster(g, cx-1, cy, 1, 0);
  338. stbcc__add_connections_to_adjacent_cluster(g, cx+1, cy, -1, 0);
  339. stbcc__add_connections_to_adjacent_cluster(g, cx, cy-1, 0, 1);
  340. stbcc__add_connections_to_adjacent_cluster(g, cx, cy+1, 0,-1);
  341. if (!g->in_batched_update)
  342. stbcc__build_connected_components_for_clumps(g);
  343. #if 0
  344. else
  345. g->cluster_dirty[cy][cx] = 1;
  346. #endif
  347. }
  348. void stbcc_update_batch_begin(stbcc_grid *g)
  349. {
  350. assert(!g->in_batched_update);
  351. g->in_batched_update = 1;
  352. }
  353. void stbcc_update_batch_end(stbcc_grid *g)
  354. {
  355. assert(g->in_batched_update);
  356. g->in_batched_update = 0;
  357. stbcc__build_connected_components_for_clumps(g); // @OPTIMIZE: only do this if update was non-empty
  358. }
  359. size_t stbcc_grid_sizeof(void)
  360. {
  361. return sizeof(stbcc_grid);
  362. }
  363. void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
  364. {
  365. int i,j,k;
  366. assert(w % STBCC__CLUSTER_SIZE_X == 0);
  367. assert(h % STBCC__CLUSTER_SIZE_Y == 0);
  368. assert(w % 8 == 0);
  369. g->w = w;
  370. g->h = h;
  371. g->cw = w >> STBCC_CLUSTER_SIZE_X_LOG2;
  372. g->ch = h >> STBCC_CLUSTER_SIZE_Y_LOG2;
  373. g->in_batched_update = 0;
  374. #if 0
  375. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j)
  376. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i)
  377. g->cluster_dirty[j][i] = 0;
  378. #endif
  379. for (j=0; j < h; ++j) {
  380. for (i=0; i < w; i += 8) {
  381. unsigned char c = 0;
  382. for (k=0; k < 8; ++k)
  383. if (map[j*w + (i+k)] == 0)
  384. c |= (1 << k);
  385. g->map[j][i>>3] = c;
  386. }
  387. }
  388. for (j=0; j < g->ch; ++j)
  389. for (i=0; i < g->cw; ++i)
  390. stbcc__build_clumps_for_cluster(g, i, j);
  391. for (j=0; j < g->ch; ++j) {
  392. for (i=0; i < g->cw; ++i) {
  393. stbcc__add_connections_to_adjacent_cluster(g, i, j, -1, 0);
  394. stbcc__add_connections_to_adjacent_cluster(g, i, j, 1, 0);
  395. stbcc__add_connections_to_adjacent_cluster(g, i, j, 0,-1);
  396. stbcc__add_connections_to_adjacent_cluster(g, i, j, 0, 1);
  397. }
  398. }
  399. stbcc__build_connected_components_for_clumps(g);
  400. for (j=0; j < g->h; ++j)
  401. for (i=0; i < g->w; ++i)
  402. assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
  403. }
  404. static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
  405. {
  406. stbcc__clump *clump;
  407. int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
  408. int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
  409. int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
  410. int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
  411. stbcc__clumpid c1 = g->clump_for_node[y1][x1];
  412. stbcc__clumpid c2 = g->clump_for_node[y2][x2];
  413. stbcc__relative_clumpid rc;
  414. assert(cx1 != cx2 || cy1 != cy2);
  415. assert(abs(cx1-cx2) + abs(cy1-cy2) == 1);
  416. // add connection to c2 in c1
  417. rc.clump_index = c2;
  418. rc.cluster_dx = x2-x1;
  419. rc.cluster_dy = y2-y1;
  420. clump = &g->cluster[cy1][cx1].clump[c1];
  421. assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
  422. clump->adjacent_clumps[clump->num_adjacent++] = rc;
  423. }
  424. static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
  425. {
  426. stbcc__clump *clump;
  427. int i;
  428. int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
  429. int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
  430. int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
  431. int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
  432. stbcc__clumpid c1 = g->clump_for_node[y1][x1];
  433. stbcc__clumpid c2 = g->clump_for_node[y2][x2];
  434. stbcc__relative_clumpid rc;
  435. assert(cx1 != cx2 || cy1 != cy2);
  436. assert(abs(cx1-cx2) + abs(cy1-cy2) == 1);
  437. // add connection to c2 in c1
  438. rc.clump_index = c2;
  439. rc.cluster_dx = x2-x1;
  440. rc.cluster_dy = y2-y1;
  441. clump = &g->cluster[cy1][cx1].clump[c1];
  442. for (i=0; i < clump->num_adjacent; ++i)
  443. if (rc.clump_index == clump->adjacent_clumps[i].clump_index &&
  444. rc.cluster_dx == clump->adjacent_clumps[i].cluster_dx &&
  445. rc.cluster_dy == clump->adjacent_clumps[i].cluster_dy)
  446. break;
  447. if (i < clump->num_adjacent)
  448. clump->adjacent_clumps[i] = clump->adjacent_clumps[--clump->num_adjacent];
  449. else
  450. assert(0);
  451. }
  452. static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
  453. {
  454. unsigned char connected[STBCC__MAX_CLUMPS_PER_CLUSTER/8] = { 0 };
  455. int x = cx * STBCC__CLUSTER_SIZE_X;
  456. int y = cy * STBCC__CLUSTER_SIZE_Y;
  457. int step_x, step_y=0, i, j, k, n;
  458. if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
  459. return;
  460. if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
  461. return;
  462. assert(abs(dx) + abs(dy) == 1);
  463. if (dx == 1) {
  464. i = STBCC__CLUSTER_SIZE_X-1;
  465. j = 0;
  466. step_x = 0;
  467. step_y = 1;
  468. n = STBCC__CLUSTER_SIZE_Y;
  469. } else if (dx == -1) {
  470. i = 0;
  471. j = 0;
  472. step_x = 0;
  473. step_y = 1;
  474. n = STBCC__CLUSTER_SIZE_Y;
  475. } else if (dy == -1) {
  476. i = 0;
  477. j = 0;
  478. step_x = 1;
  479. step_y = 0;
  480. n = STBCC__CLUSTER_SIZE_X;
  481. } else if (dy == 1) {
  482. i = 0;
  483. j = STBCC__CLUSTER_SIZE_Y-1;
  484. step_x = 1;
  485. step_y = 0;
  486. n = STBCC__CLUSTER_SIZE_X;
  487. } else {
  488. assert(0);
  489. }
  490. for (k=0; k < n; ++k) {
  491. if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
  492. stbcc__clumpid c = g->clump_for_node[y+j+dy][x+i+dx];
  493. if (0 == (connected[c>>3] & (1 << (c & 7)))) {
  494. connected[c>>3] |= 1 << (c & 7);
  495. stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
  496. }
  497. }
  498. i += step_x;
  499. j += step_y;
  500. }
  501. }
  502. static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
  503. {
  504. unsigned char disconnected[STBCC__MAX_CLUMPS_PER_CLUSTER/8] = { 0 };
  505. int x = cx * STBCC__CLUSTER_SIZE_X;
  506. int y = cy * STBCC__CLUSTER_SIZE_Y;
  507. int step_x, step_y=0, i, j, k, n;
  508. if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
  509. return;
  510. if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
  511. return;
  512. assert(abs(dx) + abs(dy) == 1);
  513. if (dx == 1) {
  514. i = STBCC__CLUSTER_SIZE_X-1;
  515. j = 0;
  516. step_x = 0;
  517. step_y = 1;
  518. n = STBCC__CLUSTER_SIZE_Y;
  519. } else if (dx == -1) {
  520. i = 0;
  521. j = 0;
  522. step_x = 0;
  523. step_y = 1;
  524. n = STBCC__CLUSTER_SIZE_Y;
  525. } else if (dy == -1) {
  526. i = 0;
  527. j = 0;
  528. step_x = 1;
  529. step_y = 0;
  530. n = STBCC__CLUSTER_SIZE_X;
  531. } else if (dy == 1) {
  532. i = 0;
  533. j = STBCC__CLUSTER_SIZE_Y-1;
  534. step_x = 1;
  535. step_y = 0;
  536. n = STBCC__CLUSTER_SIZE_X;
  537. } else {
  538. assert(0);
  539. }
  540. for (k=0; k < n; ++k) {
  541. if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
  542. stbcc__clumpid c = g->clump_for_node[y+j+dy][x+i+dx];
  543. if (0 == (disconnected[c>>3] & (1 << (c & 7)))) {
  544. disconnected[c>>3] |= 1 << (c & 7);
  545. stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
  546. }
  547. }
  548. i += step_x;
  549. j += step_y;
  550. }
  551. }
  552. static stbcc__tinypoint stbcc__incluster_find(stbcc__cluster_build_info *cbi, int x, int y)
  553. {
  554. stbcc__tinypoint p,q;
  555. p = cbi->parent[y][x];
  556. if (p.x == x && p.y == y)
  557. return p;
  558. q = stbcc__incluster_find(cbi, p.x, p.y);
  559. cbi->parent[y][x] = q;
  560. return q;
  561. }
  562. static void stbcc__incluster_union(stbcc__cluster_build_info *cbi, int x1, int y1, int x2, int y2)
  563. {
  564. stbcc__tinypoint p = stbcc__incluster_find(cbi, x1,y1);
  565. stbcc__tinypoint q = stbcc__incluster_find(cbi, x2,y2);
  566. if (p.x == q.x && p.y == q.y)
  567. return;
  568. cbi->parent[p.y][p.x] = q;
  569. }
  570. static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
  571. {
  572. stbcc__cluster *c;
  573. stbcc__cluster_build_info cbi;
  574. int label=0;
  575. int i,j;
  576. int x = cx * STBCC__CLUSTER_SIZE_X;
  577. int y = cy * STBCC__CLUSTER_SIZE_Y;
  578. // set initial disjoint set forest state
  579. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  580. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  581. cbi.parent[j][i].x = i;
  582. cbi.parent[j][i].y = j;
  583. }
  584. }
  585. // join all sets that are connected
  586. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  587. // check down only if not on bottom row
  588. if (j < STBCC__CLUSTER_SIZE_Y-1)
  589. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
  590. if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i ,y+j+1))
  591. stbcc__incluster_union(&cbi, i,j, i,j+1);
  592. // check right for everything but rightmost column
  593. for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
  594. if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j ))
  595. stbcc__incluster_union(&cbi, i,j, i+1,j);
  596. }
  597. // label all non-empty leaders
  598. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  599. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  600. stbcc__tinypoint p = cbi.parent[j][i];
  601. if (p.x == i && p.y == j)
  602. if (STBCC__MAP_OPEN(g,x+i,y+j))
  603. cbi.label[j][i] = label++;
  604. else
  605. cbi.label[j][i] = STBCC__NULL_CLUMPID;
  606. }
  607. }
  608. // label all other nodes
  609. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  610. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  611. stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
  612. if (p.x != i || p.y != j) {
  613. if (STBCC__MAP_OPEN(g,x+i,y+j))
  614. cbi.label[j][i] = cbi.label[p.y][p.x];
  615. }
  616. }
  617. }
  618. c = &g->cluster[cy][cx];
  619. c->num_clumps = label;
  620. for (i=0; i < label; ++i)
  621. c->clump[i].num_adjacent = 0;
  622. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
  623. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  624. g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
  625. assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
  626. }
  627. }
  628. #endif // STB_CONNECTED_COMPONENTS_IMPLEMENTATION