reduce storage for stb_connected_components.h

pull/286/head
Sean Barrett 2016-04-16 12:32:40 -07:00
parent 1392344cdd
commit 1d6e55ab1e
1 changed files with 167 additions and 36 deletions

View File

@ -41,7 +41,6 @@
#define INCLUDE_STB_CONNECTED_COMPONENTS_H
#include <stdlib.h>
#include <assert.h>
typedef struct st_stbcc_grid stbcc_grid;
@ -94,6 +93,9 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#ifdef STB_CONNECTED_COMPONENTS_IMPLEMENTATION
#include <assert.h>
#include <string.h> // memset
#if !defined(STBCC_GRID_COUNT_X_LOG2) || !defined(STBCC_GRID_COUNT_Y_LOG2)
#error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
#endif
@ -140,9 +142,22 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
typedef unsigned short stbcc__clumpid;
typedef unsigned char stbcc__verify_max_clumps[STBCC__MAX_CLUMPS_PER_CLUSTER < (1 << (8*sizeof(stbcc__clumpid))) ? 1 : -1];
#define STBCC__MAX_EXITS_PER_CLUMP (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y) // 64
#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
// 2^19 * 2^6 => 2^25 exits => 2^26 => 64MB for 1024x1024
// Logic for above on 4x4 grid:
//
// Many clumps: One clump:
// + + + +
// +X.X. +XX.X+
// .X.X+ .XXX
// +X.X. XXX.
// .X.X+ +X.XX+
// + + + +
//
// 8 exits either way
typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256];
typedef struct
@ -168,15 +183,20 @@ typedef union
typedef struct
{
stbcc__global_clumpid global_label;
unsigned short num_adjacent;
stbcc__relative_clumpid adjacent_clumps[STBCC__MAX_EXITS_PER_CLUMP];
} stbcc__clump;
stbcc__global_clumpid global_label; // 4
unsigned char num_adjacent; // 1
unsigned char max_adjacent; // 1
unsigned char adjacent_clump_list_index; // 1
unsigned char on_edge; // 1
} stbcc__clump; // 8
#define STBCC__CLUSTER_ADJACENCY_COUNT (STBCC__MAX_EXITS_PER_CLUSTER*4)
typedef struct
{
unsigned int num_clumps;
stbcc__clump clump[STBCC__MAX_CLUMPS_PER_CLUSTER];
unsigned char rebuild;
stbcc__clump clump[STBCC__MAX_CLUMPS_PER_CLUSTER]; // 8 * 2^9 = 4KB
stbcc__relative_clumpid adjacency_storage[STBCC__CLUSTER_ADJACENCY_COUNT]; // 512 bytes
} stbcc__cluster;
struct st_stbcc_grid
@ -184,7 +204,7 @@ struct st_stbcc_grid
int w,h,cw,ch;
unsigned char map[STBCC__GRID_COUNT_Y][STBCC__MAP_STRIDE]; // 1K x 1K => 1K x 128 => 128KB
stbcc__clumpid clump_for_node[STBCC__GRID_COUNT_Y][STBCC__GRID_COUNT_X]; // 1K x 1K x 2 = 2MB
stbcc__cluster cluster[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // 1K x 1K x 0.5 x 64 x 2 = 64MB
stbcc__cluster cluster[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // 1K x 4.5KB = 9MB
};
int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
@ -291,13 +311,15 @@ static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
for (k=0; k < (int) cluster->num_clumps; ++k) {
stbcc__clump *clump = &cluster->clump[k];
stbcc__unpacked_clumpid m;
stbcc__relative_clumpid *adj;
m.clump_index = k;
m.cluster_x = i;
m.cluster_y = j;
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (h=0; h < clump->num_adjacent; ++h) {
unsigned int clump_index = clump->adjacent_clumps[h].clump_index;
unsigned int x = clump->adjacent_clumps[h].cluster_dx + i;
unsigned int y = clump->adjacent_clumps[h].cluster_dy + j;
unsigned int clump_index = adj[h].clump_index;
unsigned int x = adj[h].cluster_dx + i;
unsigned int y = adj[h].cluster_dy + j;
stbcc__clump_union(g, m, x, y, clump_index);
}
}
@ -318,6 +340,106 @@ static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
}
}
static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int cy)
{
// in this particular case, we are fully non-incremental. that means we
// can discover the correct sizes for the arrays, but requires we build
// 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 num_adj[STBCC__MAX_CLUMPS_PER_CLUSTER] = { 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, m, dx, dy, total;
g->cluster[cy][cx].rebuild = 0;
total = 0;
for (m=0; m < 4; ++m) {
switch (m) {
case 0:
dx = 1, dy = 0;
step_x = 0, step_y = 1;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 1:
dx = -1, dy = 0;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 2:
dy = -1, dx = 0;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
case 3:
dy = 1, dx = 0;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
}
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
continue;
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]];
++total;
}
}
i += step_x;
j += step_y;
}
}
// decide how to apportion leftover... would be better if we knew WHICH clumps
// were along the edge, but we should compute this at initial time, not above
// to minimize recompoutation
total = 0;
for (i=0; i < (int) cluster->num_clumps; ++i) {
int alloc = num_adj[i]*2; // every cluster gets room for 2x current adjacency
assert(total < 256); // must fit in byte
cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
cluster->clump[i].max_adjacent = alloc;
cluster->clump[i].num_adjacent = 0;
total += alloc;
}
assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, -1, 0);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 1, 0);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0,-1);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0, 1);
// make sure all of the above succeeded.
assert(g->cluster[cy][cx].rebuild == 0);
}
static void stbcc__add_connections_to_adjacent_cluster_with_rebuild(stbcc_grid *g, int cx, int cy, int dx, int dy)
{
if (cx >= 0 && cx < g->cw && cy >= 0 && cy < g->ch) {
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
if (g->cluster[cy][cx].rebuild)
stbcc__build_all_connections_for_cluster(g, cx, cy);
}
}
void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
{
int cx,cy;
@ -344,16 +466,12 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
stbcc__build_clumps_for_cluster(g, cx, cy);
stbcc__build_all_connections_for_cluster(g, cx, cy);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, -1, 0);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 1, 0);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0,-1);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, 0, 1);
stbcc__add_connections_to_adjacent_cluster(g, cx-1, cy, 1, 0);
stbcc__add_connections_to_adjacent_cluster(g, cx+1, cy, -1, 0);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy-1, 0, 1);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy+1, 0,-1);
stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx-1, cy, 1, 0);
stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx+1, cy, -1, 0);
stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy-1, 0, 1);
stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy+1, 0,-1);
stbcc__build_connected_components_for_clumps(g);
}
@ -389,14 +507,9 @@ void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
for (i=0; i < g->cw; ++i)
stbcc__build_clumps_for_cluster(g, i, j);
for (j=0; j < g->ch; ++j) {
for (i=0; i < g->cw; ++i) {
stbcc__add_connections_to_adjacent_cluster(g, i, j, -1, 0);
stbcc__add_connections_to_adjacent_cluster(g, i, j, 1, 0);
stbcc__add_connections_to_adjacent_cluster(g, i, j, 0,-1);
stbcc__add_connections_to_adjacent_cluster(g, i, j, 0, 1);
}
}
for (j=0; j < g->ch; ++j)
for (i=0; i < g->cw; ++i)
stbcc__build_all_connections_for_cluster(g, i, j);
stbcc__build_connected_components_for_clumps(g);
@ -408,6 +521,7 @@ void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
{
stbcc__cluster *cluster;
stbcc__clump *clump;
int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
@ -429,14 +543,22 @@ static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, i
rc.cluster_dx = x2-x1;
rc.cluster_dy = y2-y1;
clump = &g->cluster[cy1][cx1].clump[c1];
assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
clump->adjacent_clumps[clump->num_adjacent++] = rc;
cluster = &g->cluster[cy1][cx1];
clump = &cluster->clump[c1];
if (clump->num_adjacent == clump->max_adjacent)
g->cluster[cy1][cx1].rebuild = 1;
else {
stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
adj[clump->num_adjacent++] = rc;
}
}
static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
{
stbcc__cluster *cluster;
stbcc__clump *clump;
stbcc__relative_clumpid *adj;
int i;
int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
@ -458,16 +580,18 @@ static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2
rc.cluster_dx = x2-x1;
rc.cluster_dy = y2-y1;
clump = &g->cluster[cy1][cx1].clump[c1];
cluster = &g->cluster[cy1][cx1];
clump = &cluster->clump[c1];
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (i=0; i < clump->num_adjacent; ++i)
if (rc.clump_index == clump->adjacent_clumps[i].clump_index &&
rc.cluster_dx == clump->adjacent_clumps[i].cluster_dx &&
rc.cluster_dy == clump->adjacent_clumps[i].cluster_dy)
if (rc.clump_index == adj[i].clump_index &&
rc.cluster_dx == adj[i].cluster_dx &&
rc.cluster_dy == adj[i].cluster_dy)
break;
if (i < clump->num_adjacent)
clump->adjacent_clumps[i] = clump->adjacent_clumps[--clump->num_adjacent];
adj[i] = adj[--clump->num_adjacent];
else
assert(0);
}
@ -485,6 +609,9 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
return;
if (g->cluster[cy][cx].rebuild)
return;
assert(abs(dx) + abs(dy) == 1);
if (dx == 1) {
@ -521,6 +648,8 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in
if (0 == (connected[c>>3] & (1 << (c & 7)))) {
connected[c>>3] |= 1 << (c & 7);
stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
if (g->cluster[cy][cx].rebuild)
break;
}
}
i += step_x;
@ -669,6 +798,8 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
}
c->rebuild = 1; // flag that it has no valid data
}
#endif // STB_CONNECTED_COMPONENTS_IMPLEMENTATION