stb_connected_components.h 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  1. // stb_connected_components - v0.96 - 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 (1<<(LOG2/2)) on each axis (e.g. 32 if LOG2=10,
  19. // 16 if LOG2=8, etc.) (You can just pad your map with untraversable space.)
  20. //
  21. // MEMORY USAGE
  22. //
  23. // Uses about 6-7 bytes per grid square (e.g. 7MB for a 1024x1024 grid).
  24. // Uses a single worst-case allocation which you pass in.
  25. //
  26. // PERFORMANCE
  27. //
  28. // On a core i7-2700K at 3.5 Ghz, for a particular 1024x1024 map (map_03.png):
  29. //
  30. // Creating map : 44.85 ms
  31. // Making one square traversable: 0.27 ms (average over 29,448 calls)
  32. // Making one square untraversable: 0.23 ms (average over 30,123 calls)
  33. // Reachability query: 0.00001 ms (average over 4,000,000 calls)
  34. //
  35. // On non-degenerate maps update time is O(N^0.5), but on degenerate maps like
  36. // checkerboards or 50% random, update time is O(N^0.75) (~2ms on above machine).
  37. //
  38. // CHANGELOG
  39. //
  40. // 0.96 (2019-03-04) Fix warnings
  41. // 0.95 (2016-10-16) Bugfix if multiple clumps in one cluster connect to same clump in another
  42. // 0.94 (2016-04-17) Bugfix & optimize worst case (checkerboard & random)
  43. // 0.93 (2016-04-16) Reduce memory by 10x for 1Kx1K map; small speedup
  44. // 0.92 (2016-04-16) Compute sqrt(N) cluster size by default
  45. // 0.91 (2016-04-15) Initial release
  46. //
  47. // TODO:
  48. // - better API documentation
  49. // - more comments
  50. // - try re-integrating naive algorithm & compare performance
  51. // - more optimized batching (current approach still recomputes local clumps many times)
  52. // - function for setting a grid of squares at once (just use batching)
  53. //
  54. // LICENSE
  55. //
  56. // See end of file for license information.
  57. //
  58. // ALGORITHM
  59. //
  60. // The NxN grid map is split into sqrt(N) x sqrt(N) blocks called
  61. // "clusters". Each cluster independently computes a set of connected
  62. // components within that cluster (ignoring all connectivity out of
  63. // that cluster) using a union-find disjoint set forest. This produces a bunch
  64. // of locally connected components called "clumps". Each clump is (a) connected
  65. // within its cluster, (b) does not directly connect to any other clumps in the
  66. // cluster (though it may connect to them by paths that lead outside the cluster,
  67. // but those are ignored at this step), and (c) maintains an adjacency list of
  68. // all clumps in adjacent clusters that it _is_ connected to. Then a second
  69. // union-find disjoint set forest is used to compute connected clumps
  70. // globally, across the whole map. Reachability is then computed by
  71. // finding which clump each input point belongs to, and checking whether
  72. // those clumps are in the same "global" connected component.
  73. //
  74. // The above data structure can be updated efficiently; on a change
  75. // of a single grid square on the map, only one cluster changes its
  76. // purely-local state, so only one cluster needs its clumps fully
  77. // recomputed. Clumps in adjacent clusters need their adjacency lists
  78. // updated: first to remove all references to the old clumps in the
  79. // rebuilt cluster, then to add new references to the new clumps. Both
  80. // of these operations can use the existing "find which clump each input
  81. // point belongs to" query to compute that adjacency information rapidly.
  82. #ifndef INCLUDE_STB_CONNECTED_COMPONENTS_H
  83. #define INCLUDE_STB_CONNECTED_COMPONENTS_H
  84. #include <stdlib.h>
  85. typedef struct st_stbcc_grid stbcc_grid;
  86. #ifdef __cplusplus
  87. extern "C" {
  88. #endif
  89. //////////////////////////////////////////////////////////////////////////////////////////
  90. //
  91. // initialization
  92. //
  93. // you allocate the grid data structure to this size (note that it will be very big!!!)
  94. extern size_t stbcc_grid_sizeof(void);
  95. // initialize the grid, value of map[] is 0 = traversable, non-0 is solid
  96. extern void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h);
  97. //////////////////////////////////////////////////////////////////////////////////////////
  98. //
  99. // main functionality
  100. //
  101. // update a grid square state, 0 = traversable, non-0 is solid
  102. // i can add a batch-update if it's needed
  103. extern void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid);
  104. // query if two grid squares are reachable from each other
  105. extern int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2);
  106. //////////////////////////////////////////////////////////////////////////////////////////
  107. //
  108. // bonus functions
  109. //
  110. // wrap multiple stbcc_update_grid calls in these function to compute
  111. // multiple updates more efficiently; cannot make queries inside batch
  112. extern void stbcc_update_batch_begin(stbcc_grid *g);
  113. extern void stbcc_update_batch_end(stbcc_grid *g);
  114. // query the grid data structure for whether a given square is open or not
  115. extern int stbcc_query_grid_open(stbcc_grid *g, int x, int y);
  116. // get a unique id for the connected component this is in; it's not necessarily
  117. // small, you'll need a hash table or something to remap it (or just use
  118. extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
  119. #define STBCC_NULL_UNIQUE_ID 0xffffffff // returned for closed map squares
  120. #ifdef __cplusplus
  121. }
  122. #endif
  123. #endif // INCLUDE_STB_CONNECTED_COMPONENTS_H
  124. #ifdef STB_CONNECTED_COMPONENTS_IMPLEMENTATION
  125. #include <assert.h>
  126. #include <string.h> // memset
  127. #if !defined(STBCC_GRID_COUNT_X_LOG2) || !defined(STBCC_GRID_COUNT_Y_LOG2)
  128. #error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
  129. #endif
  130. #define STBCC__GRID_COUNT_X (1 << STBCC_GRID_COUNT_X_LOG2)
  131. #define STBCC__GRID_COUNT_Y (1 << STBCC_GRID_COUNT_Y_LOG2)
  132. #define STBCC__MAP_STRIDE (1 << (STBCC_GRID_COUNT_X_LOG2-3))
  133. #ifndef STBCC_CLUSTER_SIZE_X_LOG2
  134. #define STBCC_CLUSTER_SIZE_X_LOG2 (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
  135. #if STBCC_CLUSTER_SIZE_X_LOG2 > 6
  136. #undef STBCC_CLUSTER_SIZE_X_LOG2
  137. #define STBCC_CLUSTER_SIZE_X_LOG2 6
  138. #endif
  139. #endif
  140. #ifndef STBCC_CLUSTER_SIZE_Y_LOG2
  141. #define STBCC_CLUSTER_SIZE_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2/2)
  142. #if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
  143. #undef STBCC_CLUSTER_SIZE_Y_LOG2
  144. #define STBCC_CLUSTER_SIZE_Y_LOG2 6
  145. #endif
  146. #endif
  147. #define STBCC__CLUSTER_SIZE_X (1 << STBCC_CLUSTER_SIZE_X_LOG2)
  148. #define STBCC__CLUSTER_SIZE_Y (1 << STBCC_CLUSTER_SIZE_Y_LOG2)
  149. #define STBCC__CLUSTER_COUNT_X_LOG2 (STBCC_GRID_COUNT_X_LOG2 - STBCC_CLUSTER_SIZE_X_LOG2)
  150. #define STBCC__CLUSTER_COUNT_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2 - STBCC_CLUSTER_SIZE_Y_LOG2)
  151. #define STBCC__CLUSTER_COUNT_X (1 << STBCC__CLUSTER_COUNT_X_LOG2)
  152. #define STBCC__CLUSTER_COUNT_Y (1 << STBCC__CLUSTER_COUNT_Y_LOG2)
  153. #if STBCC__CLUSTER_SIZE_X >= STBCC__GRID_COUNT_X || STBCC__CLUSTER_SIZE_Y >= STBCC__GRID_COUNT_Y
  154. #error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
  155. #endif
  156. // worst case # of clumps per cluster
  157. #define STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2 (STBCC_CLUSTER_SIZE_X_LOG2 + STBCC_CLUSTER_SIZE_Y_LOG2-1)
  158. #define STBCC__MAX_CLUMPS_PER_CLUSTER (1 << STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2)
  159. #define STBCC__MAX_CLUMPS (STBCC__MAX_CLUMPS_PER_CLUSTER * STBCC__CLUSTER_COUNT_X * STBCC__CLUSTER_COUNT_Y)
  160. #define STBCC__NULL_CLUMPID STBCC__MAX_CLUMPS_PER_CLUSTER
  161. #define STBCC__CLUSTER_X_FOR_COORD_X(x) ((x) >> STBCC_CLUSTER_SIZE_X_LOG2)
  162. #define STBCC__CLUSTER_Y_FOR_COORD_Y(y) ((y) >> STBCC_CLUSTER_SIZE_Y_LOG2)
  163. #define STBCC__MAP_BYTE_MASK(x,y) (1 << ((x) & 7))
  164. #define STBCC__MAP_BYTE(g,x,y) ((g)->map[y][(x) >> 3])
  165. #define STBCC__MAP_OPEN(g,x,y) (STBCC__MAP_BYTE(g,x,y) & STBCC__MAP_BYTE_MASK(x,y))
  166. typedef unsigned short stbcc__clumpid;
  167. typedef unsigned char stbcc__verify_max_clumps[STBCC__MAX_CLUMPS_PER_CLUSTER < (1 << (8*sizeof(stbcc__clumpid))) ? 1 : -1];
  168. #define STBCC__MAX_EXITS_PER_CLUSTER (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y) // 64 for 32x32
  169. #define STBCC__MAX_EXITS_PER_CLUMP (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y) // 64 for 32x32
  170. #define STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER (STBCC__MAX_EXITS_PER_CLUMP)
  171. // 2^19 * 2^6 => 2^25 exits => 2^26 => 64MB for 1024x1024
  172. // Logic for above on 4x4 grid:
  173. //
  174. // Many clumps: One clump:
  175. // + + + +
  176. // +X.X. +XX.X+
  177. // .X.X+ .XXX
  178. // +X.X. XXX.
  179. // .X.X+ +X.XX+
  180. // + + + +
  181. //
  182. // 8 exits either way
  183. typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256];
  184. typedef struct
  185. {
  186. unsigned short clump_index:12;
  187. signed short cluster_dx:2;
  188. signed short cluster_dy:2;
  189. } stbcc__relative_clumpid;
  190. typedef union
  191. {
  192. struct {
  193. unsigned int clump_index:12;
  194. unsigned int cluster_x:10;
  195. unsigned int cluster_y:10;
  196. } f;
  197. unsigned int c;
  198. } stbcc__global_clumpid;
  199. // rebuilt cluster 3,4
  200. // what changes in cluster 2,4
  201. typedef struct
  202. {
  203. stbcc__global_clumpid global_label; // 4
  204. unsigned char num_adjacent; // 1
  205. unsigned char max_adjacent; // 1
  206. unsigned char adjacent_clump_list_index; // 1
  207. unsigned char reserved;
  208. } stbcc__clump; // 8
  209. #define STBCC__CLUSTER_ADJACENCY_COUNT (STBCC__MAX_EXITS_PER_CLUSTER*2)
  210. typedef struct
  211. {
  212. short num_clumps;
  213. unsigned char num_edge_clumps;
  214. unsigned char rebuild_adjacency;
  215. stbcc__clump clump[STBCC__MAX_CLUMPS_PER_CLUSTER]; // 8 * 2^9 = 4KB
  216. stbcc__relative_clumpid adjacency_storage[STBCC__CLUSTER_ADJACENCY_COUNT]; // 256 bytes
  217. } stbcc__cluster;
  218. struct st_stbcc_grid
  219. {
  220. int w,h,cw,ch;
  221. int in_batched_update;
  222. //unsigned char cluster_dirty[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // could bitpack, but: 1K x 1K => 1KB
  223. unsigned char map[STBCC__GRID_COUNT_Y][STBCC__MAP_STRIDE]; // 1K x 1K => 1K x 128 => 128KB
  224. stbcc__clumpid clump_for_node[STBCC__GRID_COUNT_Y][STBCC__GRID_COUNT_X]; // 1K x 1K x 2 = 2MB
  225. stbcc__cluster cluster[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // 1K x 4.5KB = 4.5MB
  226. };
  227. int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
  228. {
  229. stbcc__global_clumpid label1, label2;
  230. stbcc__clumpid c1 = g->clump_for_node[y1][x1];
  231. stbcc__clumpid c2 = g->clump_for_node[y2][x2];
  232. int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
  233. int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
  234. int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
  235. int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
  236. assert(!g->in_batched_update);
  237. if (c1 == STBCC__NULL_CLUMPID || c2 == STBCC__NULL_CLUMPID)
  238. return 0;
  239. label1 = g->cluster[cy1][cx1].clump[c1].global_label;
  240. label2 = g->cluster[cy2][cx2].clump[c2].global_label;
  241. if (label1.c == label2.c)
  242. return 1;
  243. return 0;
  244. }
  245. int stbcc_query_grid_open(stbcc_grid *g, int x, int y)
  246. {
  247. return STBCC__MAP_OPEN(g, x, y) != 0;
  248. }
  249. unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y)
  250. {
  251. stbcc__clumpid c = g->clump_for_node[y][x];
  252. int cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
  253. int cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y);
  254. assert(!g->in_batched_update);
  255. if (c == STBCC__NULL_CLUMPID) return STBCC_NULL_UNIQUE_ID;
  256. return g->cluster[cy][cx].clump[c].global_label.c;
  257. }
  258. typedef struct
  259. {
  260. unsigned char x,y;
  261. } stbcc__tinypoint;
  262. typedef struct
  263. {
  264. stbcc__tinypoint parent[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X]; // 32x32 => 2KB
  265. stbcc__clumpid label[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X];
  266. } stbcc__cluster_build_info;
  267. static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy);
  268. static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy);
  269. static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy);
  270. static stbcc__global_clumpid stbcc__clump_find(stbcc_grid *g, stbcc__global_clumpid n)
  271. {
  272. stbcc__global_clumpid q;
  273. stbcc__clump *c = &g->cluster[n.f.cluster_y][n.f.cluster_x].clump[n.f.clump_index];
  274. if (c->global_label.c == n.c)
  275. return n;
  276. q = stbcc__clump_find(g, c->global_label);
  277. c->global_label = q;
  278. return q;
  279. }
  280. typedef struct
  281. {
  282. unsigned int cluster_x;
  283. unsigned int cluster_y;
  284. unsigned int clump_index;
  285. } stbcc__unpacked_clumpid;
  286. static void stbcc__clump_union(stbcc_grid *g, stbcc__unpacked_clumpid m, int x, int y, int idx)
  287. {
  288. stbcc__clump *mc = &g->cluster[m.cluster_y][m.cluster_x].clump[m.clump_index];
  289. stbcc__clump *nc = &g->cluster[y][x].clump[idx];
  290. stbcc__global_clumpid mp = stbcc__clump_find(g, mc->global_label);
  291. stbcc__global_clumpid np = stbcc__clump_find(g, nc->global_label);
  292. if (mp.c == np.c)
  293. return;
  294. g->cluster[mp.f.cluster_y][mp.f.cluster_x].clump[mp.f.clump_index].global_label = np;
  295. }
  296. static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
  297. {
  298. int i,j,k,h;
  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_edge_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. assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
  308. cluster->clump[k].global_label = m;
  309. }
  310. }
  311. }
  312. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
  313. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
  314. stbcc__cluster *cluster = &g->cluster[j][i];
  315. for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
  316. stbcc__clump *clump = &cluster->clump[k];
  317. stbcc__unpacked_clumpid m;
  318. stbcc__relative_clumpid *adj;
  319. m.clump_index = k;
  320. m.cluster_x = i;
  321. m.cluster_y = j;
  322. adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
  323. for (h=0; h < clump->num_adjacent; ++h) {
  324. unsigned int clump_index = adj[h].clump_index;
  325. unsigned int x = adj[h].cluster_dx + i;
  326. unsigned int y = adj[h].cluster_dy + j;
  327. stbcc__clump_union(g, m, x, y, clump_index);
  328. }
  329. }
  330. }
  331. }
  332. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
  333. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
  334. stbcc__cluster *cluster = &g->cluster[j][i];
  335. for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
  336. stbcc__global_clumpid m;
  337. m.f.clump_index = k;
  338. m.f.cluster_x = i;
  339. m.f.cluster_y = j;
  340. stbcc__clump_find(g, m);
  341. }
  342. }
  343. }
  344. }
  345. static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int cy)
  346. {
  347. // in this particular case, we are fully non-incremental. that means we
  348. // can discover the correct sizes for the arrays, but requires we build
  349. // the data into temporary data structures, or just count the sizes, so
  350. // for simplicity we do the latter
  351. stbcc__cluster *cluster = &g->cluster[cy][cx];
  352. unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8]; // 64 x 8 => 1KB
  353. unsigned char num_adj[STBCC__MAX_CLUMPS_PER_CLUSTER] = { 0 };
  354. int x = cx * STBCC__CLUSTER_SIZE_X;
  355. int y = cy * STBCC__CLUSTER_SIZE_Y;
  356. int step_x, step_y=0, i, j, k, n, m, dx, dy, total;
  357. int extra;
  358. g->cluster[cy][cx].rebuild_adjacency = 0;
  359. total = 0;
  360. for (m=0; m < 4; ++m) {
  361. switch (m) {
  362. case 0:
  363. dx = 1, dy = 0;
  364. step_x = 0, step_y = 1;
  365. i = STBCC__CLUSTER_SIZE_X-1;
  366. j = 0;
  367. n = STBCC__CLUSTER_SIZE_Y;
  368. break;
  369. case 1:
  370. dx = -1, dy = 0;
  371. i = 0;
  372. j = 0;
  373. step_x = 0;
  374. step_y = 1;
  375. n = STBCC__CLUSTER_SIZE_Y;
  376. break;
  377. case 2:
  378. dy = -1, dx = 0;
  379. i = 0;
  380. j = 0;
  381. step_x = 1;
  382. step_y = 0;
  383. n = STBCC__CLUSTER_SIZE_X;
  384. break;
  385. case 3:
  386. dy = 1, dx = 0;
  387. i = 0;
  388. j = STBCC__CLUSTER_SIZE_Y-1;
  389. step_x = 1;
  390. step_y = 0;
  391. n = STBCC__CLUSTER_SIZE_X;
  392. break;
  393. }
  394. if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
  395. continue;
  396. memset(connected, 0, sizeof(connected));
  397. for (k=0; k < n; ++k) {
  398. if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
  399. stbcc__clumpid src = g->clump_for_node[y+j][x+i];
  400. stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
  401. if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
  402. connected[src][dest>>3] |= 1 << (dest & 7);
  403. ++num_adj[src];
  404. ++total;
  405. }
  406. }
  407. i += step_x;
  408. j += step_y;
  409. }
  410. }
  411. assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
  412. // decide how to apportion unused adjacency slots; only clumps that lie
  413. // on the edges of the cluster need adjacency slots, so divide them up
  414. // evenly between those clumps
  415. // we want:
  416. // extra = (STBCC__CLUSTER_ADJACENCY_COUNT - total) / cluster->num_edge_clumps;
  417. // but we efficiently approximate this without a divide, because
  418. // ignoring edge-vs-non-edge with 'num_adj[i]*2' was faster than
  419. // 'num_adj[i]+extra' with the divide
  420. if (total + (cluster->num_edge_clumps<<2) <= STBCC__CLUSTER_ADJACENCY_COUNT)
  421. extra = 4;
  422. else if (total + (cluster->num_edge_clumps<<1) <= STBCC__CLUSTER_ADJACENCY_COUNT)
  423. extra = 2;
  424. else if (total + (cluster->num_edge_clumps<<0) <= STBCC__CLUSTER_ADJACENCY_COUNT)
  425. extra = 1;
  426. else
  427. extra = 0;
  428. total = 0;
  429. for (i=0; i < (int) cluster->num_edge_clumps; ++i) {
  430. int alloc = num_adj[i]+extra;
  431. if (alloc > STBCC__MAX_EXITS_PER_CLUSTER)
  432. alloc = STBCC__MAX_EXITS_PER_CLUSTER;
  433. assert(total < 256); // must fit in byte
  434. cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
  435. cluster->clump[i].max_adjacent = alloc;
  436. cluster->clump[i].num_adjacent = 0;
  437. total += alloc;
  438. }
  439. assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
  440. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, -1, 0);
  441. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 1, 0);
  442. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0,-1);
  443. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0, 1);
  444. // make sure all of the above succeeded.
  445. assert(g->cluster[cy][cx].rebuild_adjacency == 0);
  446. }
  447. static void stbcc__add_connections_to_adjacent_cluster_with_rebuild(stbcc_grid *g, int cx, int cy, int dx, int dy)
  448. {
  449. if (cx >= 0 && cx < g->cw && cy >= 0 && cy < g->ch) {
  450. stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
  451. if (g->cluster[cy][cx].rebuild_adjacency)
  452. stbcc__build_all_connections_for_cluster(g, cx, cy);
  453. }
  454. }
  455. void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
  456. {
  457. int cx,cy;
  458. if (!solid) {
  459. if (STBCC__MAP_OPEN(g,x,y))
  460. return;
  461. } else {
  462. if (!STBCC__MAP_OPEN(g,x,y))
  463. return;
  464. }
  465. cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
  466. cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y);
  467. stbcc__remove_connections_to_adjacent_cluster(g, cx-1, cy, 1, 0);
  468. stbcc__remove_connections_to_adjacent_cluster(g, cx+1, cy, -1, 0);
  469. stbcc__remove_connections_to_adjacent_cluster(g, cx, cy-1, 0, 1);
  470. stbcc__remove_connections_to_adjacent_cluster(g, cx, cy+1, 0,-1);
  471. if (!solid)
  472. STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
  473. else
  474. STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
  475. stbcc__build_clumps_for_cluster(g, cx, cy);
  476. stbcc__build_all_connections_for_cluster(g, cx, cy);
  477. stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx-1, cy, 1, 0);
  478. stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx+1, cy, -1, 0);
  479. stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy-1, 0, 1);
  480. stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy+1, 0,-1);
  481. if (!g->in_batched_update)
  482. stbcc__build_connected_components_for_clumps(g);
  483. #if 0
  484. else
  485. g->cluster_dirty[cy][cx] = 1;
  486. #endif
  487. }
  488. void stbcc_update_batch_begin(stbcc_grid *g)
  489. {
  490. assert(!g->in_batched_update);
  491. g->in_batched_update = 1;
  492. }
  493. void stbcc_update_batch_end(stbcc_grid *g)
  494. {
  495. assert(g->in_batched_update);
  496. g->in_batched_update = 0;
  497. stbcc__build_connected_components_for_clumps(g); // @OPTIMIZE: only do this if update was non-empty
  498. }
  499. size_t stbcc_grid_sizeof(void)
  500. {
  501. return sizeof(stbcc_grid);
  502. }
  503. void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
  504. {
  505. int i,j,k;
  506. assert(w % STBCC__CLUSTER_SIZE_X == 0);
  507. assert(h % STBCC__CLUSTER_SIZE_Y == 0);
  508. assert(w % 8 == 0);
  509. g->w = w;
  510. g->h = h;
  511. g->cw = w >> STBCC_CLUSTER_SIZE_X_LOG2;
  512. g->ch = h >> STBCC_CLUSTER_SIZE_Y_LOG2;
  513. g->in_batched_update = 0;
  514. #if 0
  515. for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j)
  516. for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i)
  517. g->cluster_dirty[j][i] = 0;
  518. #endif
  519. for (j=0; j < h; ++j) {
  520. for (i=0; i < w; i += 8) {
  521. unsigned char c = 0;
  522. for (k=0; k < 8; ++k)
  523. if (map[j*w + (i+k)] == 0)
  524. c |= (1 << k);
  525. g->map[j][i>>3] = c;
  526. }
  527. }
  528. for (j=0; j < g->ch; ++j)
  529. for (i=0; i < g->cw; ++i)
  530. stbcc__build_clumps_for_cluster(g, i, j);
  531. for (j=0; j < g->ch; ++j)
  532. for (i=0; i < g->cw; ++i)
  533. stbcc__build_all_connections_for_cluster(g, i, j);
  534. stbcc__build_connected_components_for_clumps(g);
  535. for (j=0; j < g->h; ++j)
  536. for (i=0; i < g->w; ++i)
  537. assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
  538. }
  539. static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
  540. {
  541. stbcc__cluster *cluster;
  542. stbcc__clump *clump;
  543. int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
  544. int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
  545. int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
  546. int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
  547. stbcc__clumpid c1 = g->clump_for_node[y1][x1];
  548. stbcc__clumpid c2 = g->clump_for_node[y2][x2];
  549. stbcc__relative_clumpid rc;
  550. assert(cx1 != cx2 || cy1 != cy2);
  551. assert(abs(cx1-cx2) + abs(cy1-cy2) == 1);
  552. // add connection to c2 in c1
  553. rc.clump_index = c2;
  554. rc.cluster_dx = x2-x1;
  555. rc.cluster_dy = y2-y1;
  556. cluster = &g->cluster[cy1][cx1];
  557. clump = &cluster->clump[c1];
  558. assert(clump->num_adjacent <= clump->max_adjacent);
  559. if (clump->num_adjacent == clump->max_adjacent)
  560. g->cluster[cy1][cx1].rebuild_adjacency = 1;
  561. else {
  562. stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
  563. assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
  564. assert(clump->adjacent_clump_list_index + clump->num_adjacent <= STBCC__CLUSTER_ADJACENCY_COUNT);
  565. adj[clump->num_adjacent++] = rc;
  566. }
  567. }
  568. static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
  569. {
  570. stbcc__cluster *cluster;
  571. stbcc__clump *clump;
  572. stbcc__relative_clumpid *adj;
  573. int i;
  574. int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
  575. int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
  576. int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
  577. int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
  578. stbcc__clumpid c1 = g->clump_for_node[y1][x1];
  579. stbcc__clumpid c2 = g->clump_for_node[y2][x2];
  580. stbcc__relative_clumpid rc;
  581. assert(cx1 != cx2 || cy1 != cy2);
  582. assert(abs(cx1-cx2) + abs(cy1-cy2) == 1);
  583. // add connection to c2 in c1
  584. rc.clump_index = c2;
  585. rc.cluster_dx = x2-x1;
  586. rc.cluster_dy = y2-y1;
  587. cluster = &g->cluster[cy1][cx1];
  588. clump = &cluster->clump[c1];
  589. adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
  590. for (i=0; i < clump->num_adjacent; ++i)
  591. if (rc.clump_index == adj[i].clump_index &&
  592. rc.cluster_dx == adj[i].cluster_dx &&
  593. rc.cluster_dy == adj[i].cluster_dy)
  594. break;
  595. if (i < clump->num_adjacent)
  596. adj[i] = adj[--clump->num_adjacent];
  597. else
  598. assert(0);
  599. }
  600. static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
  601. {
  602. unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { { 0 } };
  603. int x = cx * STBCC__CLUSTER_SIZE_X;
  604. int y = cy * STBCC__CLUSTER_SIZE_Y;
  605. int step_x, step_y=0, i, j, k, n;
  606. if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
  607. return;
  608. if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
  609. return;
  610. if (g->cluster[cy][cx].rebuild_adjacency)
  611. return;
  612. assert(abs(dx) + abs(dy) == 1);
  613. if (dx == 1) {
  614. i = STBCC__CLUSTER_SIZE_X-1;
  615. j = 0;
  616. step_x = 0;
  617. step_y = 1;
  618. n = STBCC__CLUSTER_SIZE_Y;
  619. } else if (dx == -1) {
  620. i = 0;
  621. j = 0;
  622. step_x = 0;
  623. step_y = 1;
  624. n = STBCC__CLUSTER_SIZE_Y;
  625. } else if (dy == -1) {
  626. i = 0;
  627. j = 0;
  628. step_x = 1;
  629. step_y = 0;
  630. n = STBCC__CLUSTER_SIZE_X;
  631. } else if (dy == 1) {
  632. i = 0;
  633. j = STBCC__CLUSTER_SIZE_Y-1;
  634. step_x = 1;
  635. step_y = 0;
  636. n = STBCC__CLUSTER_SIZE_X;
  637. } else {
  638. assert(0);
  639. return;
  640. }
  641. for (k=0; k < n; ++k) {
  642. if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
  643. stbcc__clumpid src = g->clump_for_node[y+j][x+i];
  644. stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
  645. if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
  646. assert((dest>>3) < sizeof(connected));
  647. connected[src][dest>>3] |= 1 << (dest & 7);
  648. stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
  649. if (g->cluster[cy][cx].rebuild_adjacency)
  650. break;
  651. }
  652. }
  653. i += step_x;
  654. j += step_y;
  655. }
  656. }
  657. static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
  658. {
  659. unsigned char disconnected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { { 0 } };
  660. int x = cx * STBCC__CLUSTER_SIZE_X;
  661. int y = cy * STBCC__CLUSTER_SIZE_Y;
  662. int step_x, step_y=0, i, j, k, n;
  663. if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
  664. return;
  665. if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
  666. return;
  667. assert(abs(dx) + abs(dy) == 1);
  668. if (dx == 1) {
  669. i = STBCC__CLUSTER_SIZE_X-1;
  670. j = 0;
  671. step_x = 0;
  672. step_y = 1;
  673. n = STBCC__CLUSTER_SIZE_Y;
  674. } else if (dx == -1) {
  675. i = 0;
  676. j = 0;
  677. step_x = 0;
  678. step_y = 1;
  679. n = STBCC__CLUSTER_SIZE_Y;
  680. } else if (dy == -1) {
  681. i = 0;
  682. j = 0;
  683. step_x = 1;
  684. step_y = 0;
  685. n = STBCC__CLUSTER_SIZE_X;
  686. } else if (dy == 1) {
  687. i = 0;
  688. j = STBCC__CLUSTER_SIZE_Y-1;
  689. step_x = 1;
  690. step_y = 0;
  691. n = STBCC__CLUSTER_SIZE_X;
  692. } else {
  693. assert(0);
  694. return;
  695. }
  696. for (k=0; k < n; ++k) {
  697. if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
  698. stbcc__clumpid src = g->clump_for_node[y+j][x+i];
  699. stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
  700. if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) {
  701. disconnected[src][dest>>3] |= 1 << (dest & 7);
  702. stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
  703. }
  704. }
  705. i += step_x;
  706. j += step_y;
  707. }
  708. }
  709. static stbcc__tinypoint stbcc__incluster_find(stbcc__cluster_build_info *cbi, int x, int y)
  710. {
  711. stbcc__tinypoint p,q;
  712. p = cbi->parent[y][x];
  713. if (p.x == x && p.y == y)
  714. return p;
  715. q = stbcc__incluster_find(cbi, p.x, p.y);
  716. cbi->parent[y][x] = q;
  717. return q;
  718. }
  719. static void stbcc__incluster_union(stbcc__cluster_build_info *cbi, int x1, int y1, int x2, int y2)
  720. {
  721. stbcc__tinypoint p = stbcc__incluster_find(cbi, x1,y1);
  722. stbcc__tinypoint q = stbcc__incluster_find(cbi, x2,y2);
  723. if (p.x == q.x && p.y == q.y)
  724. return;
  725. cbi->parent[p.y][p.x] = q;
  726. }
  727. static void stbcc__switch_root(stbcc__cluster_build_info *cbi, int x, int y, stbcc__tinypoint p)
  728. {
  729. cbi->parent[p.y][p.x].x = x;
  730. cbi->parent[p.y][p.x].y = y;
  731. cbi->parent[y][x].x = x;
  732. cbi->parent[y][x].y = y;
  733. }
  734. static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
  735. {
  736. stbcc__cluster *c;
  737. stbcc__cluster_build_info cbi;
  738. int label=0;
  739. int i,j;
  740. int x = cx * STBCC__CLUSTER_SIZE_X;
  741. int y = cy * STBCC__CLUSTER_SIZE_Y;
  742. // set initial disjoint set forest state
  743. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  744. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  745. cbi.parent[j][i].x = i;
  746. cbi.parent[j][i].y = j;
  747. }
  748. }
  749. // join all sets that are connected
  750. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  751. // check down only if not on bottom row
  752. if (j < STBCC__CLUSTER_SIZE_Y-1)
  753. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
  754. if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i ,y+j+1))
  755. stbcc__incluster_union(&cbi, i,j, i,j+1);
  756. // check right for everything but rightmost column
  757. for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
  758. if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j ))
  759. stbcc__incluster_union(&cbi, i,j, i+1,j);
  760. }
  761. // label all non-empty clumps along edges so that all edge clumps are first
  762. // in list; this means in degenerate case we can skip traversing non-edge clumps.
  763. // because in the first pass we only label leaders, we swap the leader to the
  764. // edge first
  765. // first put solid labels on all the edges; these will get overwritten if they're open
  766. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
  767. cbi.label[j][0] = cbi.label[j][STBCC__CLUSTER_SIZE_X-1] = STBCC__NULL_CLUMPID;
  768. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
  769. cbi.label[0][i] = cbi.label[STBCC__CLUSTER_SIZE_Y-1][i] = STBCC__NULL_CLUMPID;
  770. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  771. i = 0;
  772. if (STBCC__MAP_OPEN(g, x+i, y+j)) {
  773. stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
  774. if (p.x == i && p.y == j)
  775. // if this is the leader, give it a label
  776. cbi.label[j][i] = label++;
  777. else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
  778. // if leader is in interior, promote this edge node to leader and label
  779. stbcc__switch_root(&cbi, i, j, p);
  780. cbi.label[j][i] = label++;
  781. }
  782. // else if leader is on edge, do nothing (it'll get labelled when we reach it)
  783. }
  784. i = STBCC__CLUSTER_SIZE_X-1;
  785. if (STBCC__MAP_OPEN(g, x+i, y+j)) {
  786. stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
  787. if (p.x == i && p.y == j)
  788. cbi.label[j][i] = label++;
  789. else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
  790. stbcc__switch_root(&cbi, i, j, p);
  791. cbi.label[j][i] = label++;
  792. }
  793. }
  794. }
  795. for (i=1; i < STBCC__CLUSTER_SIZE_Y-1; ++i) {
  796. j = 0;
  797. if (STBCC__MAP_OPEN(g, x+i, y+j)) {
  798. stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
  799. if (p.x == i && p.y == j)
  800. cbi.label[j][i] = label++;
  801. else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
  802. stbcc__switch_root(&cbi, i, j, p);
  803. cbi.label[j][i] = label++;
  804. }
  805. }
  806. j = STBCC__CLUSTER_SIZE_Y-1;
  807. if (STBCC__MAP_OPEN(g, x+i, y+j)) {
  808. stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
  809. if (p.x == i && p.y == j)
  810. cbi.label[j][i] = label++;
  811. else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
  812. stbcc__switch_root(&cbi, i, j, p);
  813. cbi.label[j][i] = label++;
  814. }
  815. }
  816. }
  817. c = &g->cluster[cy][cx];
  818. c->num_edge_clumps = label;
  819. // label any internal clusters
  820. for (j=1; j < STBCC__CLUSTER_SIZE_Y-1; ++j) {
  821. for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
  822. stbcc__tinypoint p = cbi.parent[j][i];
  823. if (p.x == i && p.y == j) {
  824. if (STBCC__MAP_OPEN(g,x+i,y+j))
  825. cbi.label[j][i] = label++;
  826. else
  827. cbi.label[j][i] = STBCC__NULL_CLUMPID;
  828. }
  829. }
  830. }
  831. // label all other nodes
  832. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
  833. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  834. stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
  835. if (p.x != i || p.y != j) {
  836. if (STBCC__MAP_OPEN(g,x+i,y+j))
  837. cbi.label[j][i] = cbi.label[p.y][p.x];
  838. }
  839. if (STBCC__MAP_OPEN(g,x+i,y+j))
  840. assert(cbi.label[j][i] != STBCC__NULL_CLUMPID);
  841. }
  842. }
  843. c->num_clumps = label;
  844. for (i=0; i < label; ++i) {
  845. c->clump[i].num_adjacent = 0;
  846. c->clump[i].max_adjacent = 0;
  847. }
  848. for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
  849. for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
  850. g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
  851. assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
  852. }
  853. // set the global label for all interior clumps since they can't have connections,
  854. // so we don't have to do this on the global pass (brings from O(N) to O(N^0.75))
  855. for (i=(int) c->num_edge_clumps; i < (int) c->num_clumps; ++i) {
  856. stbcc__global_clumpid gc;
  857. gc.f.cluster_x = cx;
  858. gc.f.cluster_y = cy;
  859. gc.f.clump_index = i;
  860. c->clump[i].global_label = gc;
  861. }
  862. c->rebuild_adjacency = 1; // flag that it has no valid adjacency data
  863. }
  864. #endif // STB_CONNECTED_COMPONENTS_IMPLEMENTATION
  865. /*
  866. ------------------------------------------------------------------------------
  867. This software is available under 2 licenses -- choose whichever you prefer.
  868. ------------------------------------------------------------------------------
  869. ALTERNATIVE A - MIT License
  870. Copyright (c) 2017 Sean Barrett
  871. Permission is hereby granted, free of charge, to any person obtaining a copy of
  872. this software and associated documentation files (the "Software"), to deal in
  873. the Software without restriction, including without limitation the rights to
  874. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  875. of the Software, and to permit persons to whom the Software is furnished to do
  876. so, subject to the following conditions:
  877. The above copyright notice and this permission notice shall be included in all
  878. copies or substantial portions of the Software.
  879. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  880. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  881. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  882. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  883. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  884. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  885. SOFTWARE.
  886. ------------------------------------------------------------------------------
  887. ALTERNATIVE B - Public Domain (www.unlicense.org)
  888. This is free and unencumbered software released into the public domain.
  889. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
  890. software, either in source code form or as a compiled binary, for any purpose,
  891. commercial or non-commercial, and by any means.
  892. In jurisdictions that recognize copyright laws, the author or authors of this
  893. software dedicate any and all copyright interest in the software to the public
  894. domain. We make this dedication for the benefit of the public at large and to
  895. the detriment of our heirs and successors. We intend this dedication to be an
  896. overt act of relinquishment in perpetuity of all present and future rights to
  897. this software under copyright law.
  898. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  899. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  900. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  901. AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  902. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  903. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  904. ------------------------------------------------------------------------------
  905. */