diff --git a/stb_connected_components.h b/stb_connected_components.h index abe2d8e..778b5f9 100644 --- a/stb_connected_components.h +++ b/stb_connected_components.h @@ -1,4 +1,4 @@ -// stb_connected_components - v0.94 - public domain connected components on grids +// stb_connected_components - v0.95 - public domain connected components on grids // http://github.com/nothings/stb // // Finds connected components on 2D grids for testing reachability between @@ -37,6 +37,7 @@ // // CHANGELOG // +// 0.95 (2016-10-16) Bugfix if multiple clumps in one cluster connect to same clump in another // 0.94 (2016-04-17) Bugfix & optimize worst case (checkerboard & random) // 0.93 (2016-04-16) Reduce memory by 10x for 1Kx1K map; small speedup // 0.92 (2016-04-16) Compute sqrt(N) cluster size by default @@ -201,6 +202,8 @@ typedef unsigned char stbcc__verify_max_clumps[STBCC__MAX_CLUMPS_PER_CLUSTER < ( #define STBCC__MAX_EXITS_PER_CLUSTER (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y) // 64 for 32x32 #define STBCC__MAX_EXITS_PER_CLUMP (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y) // 64 for 32x32 +#define STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER (STBCC__MAX_EXITS_PER_CLUMP) + // 2^19 * 2^6 => 2^25 exits => 2^26 => 64MB for 1024x1024 // Logic for above on 4x4 grid: @@ -409,7 +412,7 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int // the data into temporary data structures, or just count the sizes, so // for simplicity we do the latter stbcc__cluster *cluster = &g->cluster[cy][cx]; - unsigned char connected[STBCC__MAX_CLUMPS_PER_CLUSTER/8]; + unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8]; // 64 x 8 => 1KB unsigned char num_adj[STBCC__MAX_CLUMPS_PER_CLUSTER] = { 0 }; int x = cx * STBCC__CLUSTER_SIZE_X; int y = cy * STBCC__CLUSTER_SIZE_Y; @@ -460,10 +463,11 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int memset(connected, 0, sizeof(connected)); for (k=0; k < n; ++k) { if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) { - stbcc__clumpid c = g->clump_for_node[y+j+dy][x+i+dx]; - if (0 == (connected[c>>3] & (1 << (c & 7)))) { - connected[c>>3] |= 1 << (c & 7); - ++num_adj[g->clump_for_node[y+j][x+i]]; + stbcc__clumpid src = g->clump_for_node[y+j][x+i]; + stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx]; + if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) { + connected[src][dest>>3] |= 1 << (dest & 7); + ++num_adj[src]; ++total; } } @@ -707,7 +711,7 @@ static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2 static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy) { - unsigned char connected[STBCC__MAX_CLUMPS_PER_CLUSTER/8] = { 0 }; + unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { 0 }; int x = cx * STBCC__CLUSTER_SIZE_X; int y = cy * STBCC__CLUSTER_SIZE_Y; int step_x, step_y=0, i, j, k, n; @@ -753,10 +757,11 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in for (k=0; k < n; ++k) { if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) { - stbcc__clumpid c = g->clump_for_node[y+j+dy][x+i+dx]; - if (0 == (connected[c>>3] & (1 << (c & 7)))) { - assert((c>>3) < sizeof(connected)); - connected[c>>3] |= 1 << (c & 7); + stbcc__clumpid src = g->clump_for_node[y+j][x+i]; + stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx]; + if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) { + assert((dest>>3) < sizeof(connected)); + connected[src][dest>>3] |= 1 << (dest & 7); stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy); if (g->cluster[cy][cx].rebuild_adjacency) break; @@ -769,7 +774,7 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy) { - unsigned char disconnected[STBCC__MAX_CLUMPS_PER_CLUSTER/8] = { 0 }; + unsigned char disconnected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { 0 }; int x = cx * STBCC__CLUSTER_SIZE_X; int y = cy * STBCC__CLUSTER_SIZE_Y; int step_x, step_y=0, i, j, k, n; @@ -812,9 +817,10 @@ static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, for (k=0; k < n; ++k) { if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) { - stbcc__clumpid c = g->clump_for_node[y+j+dy][x+i+dx]; - if (0 == (disconnected[c>>3] & (1 << (c & 7)))) { - disconnected[c>>3] |= 1 << (c & 7); + stbcc__clumpid src = g->clump_for_node[y+j][x+i]; + stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx]; + if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) { + disconnected[src][dest>>3] |= 1 << (dest & 7); stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy); } } diff --git a/tests/grid_reachability.c b/tests/grid_reachability.c index 8314d00..2bb1cd8 100644 --- a/tests/grid_reachability.c +++ b/tests/grid_reachability.c @@ -96,14 +96,24 @@ void write_map(stbcc_grid *g, int w, int h, char *filename) for (i=0; i < w; ++i) { unsigned int c; c = stbcc_get_unique_id(g,i,j); + c = stb_rehash_improved(c)&0xffffff; if (c == STBCC_NULL_UNIQUE_ID) c = 0xff000000; else c = (~c)^0x555555; + if (i % 32 == 0 || j %32 == 0) { + int r = (c >> 16) & 255; + int g = (c >> 8) & 255; + int b = c & 255; + r = (r+130)/2; + g = (g+130)/2; + b = (b+130)/2; + c = 0xff000000 + (r<<16) + (g<<8) + b; + } color[j][i] = c; } } - stbi_write_png(filename, w, h, 4, color, 0); + stbi_write_png(filename, w, h, 4, color, 4*w); } void test_connected(stbcc_grid *g) @@ -159,18 +169,43 @@ int main(int argc, char **argv) map[(stb_rand()%h)*w + stb_rand()%w] ^= 255; #endif + _mkdir("tests/output/stbcc"); + stbi_write_png("tests/output/stbcc/reference.png", w, h, 1, map, 0); //reference(map, w, h); - _mkdir("tests/output/stbcc"); - g = malloc(stbcc_grid_sizeof()); printf("Size: %d\n", stbcc_grid_sizeof()); +#if 0 + memset(map, 0, w*h); + stbcc_init_grid(g, map, w, h); + { + int n; + char **s = stb_stringfile("c:/x/clockwork_update.txt", &n); + write_map(g, w, h, "tests/output/stbcc/base.png"); + for (i=1; i < n; i += 1) { + int x,y,t; + sscanf(s[i], "%d %d %d", &x, &y, &t); + if (i == 571678) + write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_good.png", i)); + stbcc_update_grid(g, x, y, t); + if (i == 571678) + write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_bad.png", i)); + //if (i > 571648 && i <= 571712) + //write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i)); + } + write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i-1)); + } + return 0; +#endif + + start_timer("init"); stbcc_init_grid(g, map, w, h); end_timer(); + //g = stb_file("c:/x/clockwork_path.bin", 0); write_map(g, w, h, "tests/output/stbcc/base.png"); for (i=0; i < 5000;) {