parent
							
								
									4c519106a7
								
							
						
					
					
						commit
						1c5a92440d
					
				
				 3 changed files with 657 additions and 2 deletions
			
			
		| @ -0,0 +1,653 @@ | |||||||
|  | // stb_connected_components - v0.90 - public domain connected components on grids
 | ||||||
|  | //                                                 http://github.com/nothings/stb
 | ||||||
|  | //
 | ||||||
|  | // Finds connected components on 2D grids for testing reachability between
 | ||||||
|  | // two points, with fast updates when changing reachability (e.g. on one machine
 | ||||||
|  | // it was typically 0.2ms w/ 1024x1024 grid). Each grid square must be "open" or
 | ||||||
|  | // "closed" (traversable or untraversable), and grid squares are only connected
 | ||||||
|  | // to their orthogonal neighbors, not diagonally. 
 | ||||||
|  | //
 | ||||||
|  | // In one source file, create the implementation by doing something like this:
 | ||||||
|  | //
 | ||||||
|  | //   #define STBCC_GRID_COUNT_X_LOG2    10
 | ||||||
|  | //   #define STBCC_GRID_COUNT_Y_LOG2    10
 | ||||||
|  | //   #define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
 | ||||||
|  | //   #include "stb_connected_components.h"
 | ||||||
|  | //
 | ||||||
|  | // The above creates an implementation that can run on maps up to 1024x1024.
 | ||||||
|  | // Map sizes must be a multiple of 32 on each axis.
 | ||||||
|  | // 
 | ||||||
|  | // LICENSE
 | ||||||
|  | // 
 | ||||||
|  | // This software is dual-licensed to the public domain and under the following
 | ||||||
|  | // license: you are granted a perpetual, irrevocable license to copy, modify,
 | ||||||
|  | // publish, and distribute this file as you see fit.
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | #ifndef INCLUDE_STB_CONNECTED_COMPONENTS_H | ||||||
|  | #define INCLUDE_STB_CONNECTED_COMPONENTS_H | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | typedef struct st_stbcc_grid stbcc_grid; | ||||||
|  | 
 | ||||||
|  | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | //
 | ||||||
|  | //  initialization
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | // you allocate the grid data structure to this size (note that it will be very big!!!)
 | ||||||
|  | extern size_t stbcc_grid_sizeof(void); | ||||||
|  | 
 | ||||||
|  | // initialize the grid, value of map[] is 0 = traversable, non-0 is solid
 | ||||||
|  | extern void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | //
 | ||||||
|  | //  main functionality
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | // update a grid square state, 0 = traversable, non-0 is solid
 | ||||||
|  | // i can add a batch-update if it's needed
 | ||||||
|  | extern void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid); | ||||||
|  | 
 | ||||||
|  | // query if two grid squares are reachable from each other
 | ||||||
|  | extern int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | //
 | ||||||
|  | //  bonus functions
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | // query the grid data structure for whether a given square is open or not
 | ||||||
|  | extern int stbcc_query_grid_open(stbcc_grid *g, int x, int y); | ||||||
|  | 
 | ||||||
|  | // get a unique id for the connected component this is in; it's not necessarily
 | ||||||
|  | // small, you'll need a hash table or something to remap it (or just use
 | ||||||
|  | extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y); | ||||||
|  | 
 | ||||||
|  | #endif // INCLUDE_STB_CONNECTED_COMPONENTS_H
 | ||||||
|  | 
 | ||||||
|  | #ifdef STB_CONNECTED_COMPONENTS_IMPLEMENTATION | ||||||
|  | 
 | ||||||
|  | #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 | ||||||
|  | 
 | ||||||
|  | #define STBCC__GRID_COUNT_X (1 << STBCC_GRID_COUNT_X_LOG2) | ||||||
|  | #define STBCC__GRID_COUNT_Y (1 << STBCC_GRID_COUNT_Y_LOG2) | ||||||
|  | 
 | ||||||
|  | #define STBCC__MAP_STRIDE   (1 << (STBCC_GRID_COUNT_X_LOG2-3)) | ||||||
|  | 
 | ||||||
|  | #ifndef STBCC_CLUSTER_SIZE_X_LOG2 | ||||||
|  | #define STBCC_CLUSTER_SIZE_X_LOG2   5 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef STBCC_CLUSTER_SIZE_Y_LOG2 | ||||||
|  | #define STBCC_CLUSTER_SIZE_Y_LOG2   5 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define STBCC__CLUSTER_SIZE_X   (1 << STBCC_CLUSTER_SIZE_X_LOG2) | ||||||
|  | #define STBCC__CLUSTER_SIZE_Y   (1 << STBCC_CLUSTER_SIZE_Y_LOG2) | ||||||
|  | 
 | ||||||
|  | #define STBCC__CLUSTER_COUNT_X_LOG2   (STBCC_GRID_COUNT_X_LOG2 - STBCC_CLUSTER_SIZE_X_LOG2) | ||||||
|  | #define STBCC__CLUSTER_COUNT_Y_LOG2   (STBCC_GRID_COUNT_Y_LOG2 - STBCC_CLUSTER_SIZE_Y_LOG2) | ||||||
|  | 
 | ||||||
|  | #define STBCC__CLUSTER_COUNT_X  (1 << STBCC__CLUSTER_COUNT_X_LOG2) | ||||||
|  | #define STBCC__CLUSTER_COUNT_Y  (1 << STBCC__CLUSTER_COUNT_Y_LOG2) | ||||||
|  | 
 | ||||||
|  | #if STBCC__CLUSTER_SIZE_X >= STBCC__GRID_COUNT_X || STBCC__CLUSTER_SIZE_Y >= STBCC__GRID_COUNT_Y | ||||||
|  |    #error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // worst case # of clumps per cluster
 | ||||||
|  | #define STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2   (STBCC_CLUSTER_SIZE_X_LOG2 + STBCC_CLUSTER_SIZE_Y_LOG2-1) | ||||||
|  | #define STBCC__MAX_CLUMPS_PER_CLUSTER        (1 << STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2) | ||||||
|  | #define STBCC__MAX_CLUMPS                    (STBCC__MAX_CLUMPS_PER_CLUSTER * STBCC__CLUSTER_COUNT_X * STBCC__CLUSTER_COUNT_Y) | ||||||
|  | #define STBCC__NULL_CLUMPID                  STBCC__MAX_CLUMPS_PER_CLUSTER | ||||||
|  | 
 | ||||||
|  | #define STBCC__CLUSTER_X_FOR_COORD_X(x)  ((x) >> STBCC_CLUSTER_SIZE_X_LOG2) | ||||||
|  | #define STBCC__CLUSTER_Y_FOR_COORD_Y(y)  ((y) >> STBCC_CLUSTER_SIZE_Y_LOG2) | ||||||
|  | 
 | ||||||
|  | #define STBCC__MAP_BYTE_MASK(x,y)       (1 << ((x) & 7)) | ||||||
|  | #define STBCC__MAP_BYTE(g,x,y)          ((g)->map[y][(x) >> 3]) | ||||||
|  | #define STBCC__MAP_OPEN(g,x,y)          (STBCC__MAP_BYTE(g,x,y) & STBCC__MAP_BYTE_MASK(x,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
 | ||||||
|  | // 2^19 * 2^6 => 2^25 exits => 2^26  => 64MB for 1024x1024
 | ||||||
|  | 
 | ||||||
|  | typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256]; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    unsigned short clump_index:12; | ||||||
|  |      signed short cluster_dx:2; | ||||||
|  |      signed short cluster_dy:2; | ||||||
|  | } stbcc__relative_clumpid; | ||||||
|  | 
 | ||||||
|  | typedef union | ||||||
|  | { | ||||||
|  |    struct { | ||||||
|  |       unsigned int clump_index:12; | ||||||
|  |       unsigned int cluster_x:10; | ||||||
|  |       unsigned int cluster_y:10; | ||||||
|  |    } f; | ||||||
|  |    unsigned int c; | ||||||
|  | } stbcc__global_clumpid; | ||||||
|  | 
 | ||||||
|  | // rebuilt cluster 3,4
 | ||||||
|  | 
 | ||||||
|  | // what changes in cluster 2,4
 | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    stbcc__global_clumpid global_label; | ||||||
|  |    unsigned char num_adjacent; | ||||||
|  |    stbcc__relative_clumpid adjacent_clumps[STBCC__MAX_EXITS_PER_CLUMP]; | ||||||
|  | } stbcc__clump; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    unsigned int num_clumps; | ||||||
|  |    stbcc__clump clump[STBCC__MAX_CLUMPS_PER_CLUSTER]; | ||||||
|  | } stbcc__cluster; | ||||||
|  | 
 | ||||||
|  | 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
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2) | ||||||
|  | { | ||||||
|  |    stbcc__global_clumpid label1, label2; | ||||||
|  |    stbcc__clumpid c1 = g->clump_for_node[y1][x1]; | ||||||
|  |    stbcc__clumpid c2 = g->clump_for_node[y2][x2]; | ||||||
|  |    int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1); | ||||||
|  |    int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1); | ||||||
|  |    int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2); | ||||||
|  |    int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2); | ||||||
|  |    if (c1 == STBCC__NULL_CLUMPID || c2 == STBCC__NULL_CLUMPID) | ||||||
|  |       return 0; | ||||||
|  |    label1 = g->cluster[cy1][cx1].clump[c1].global_label; | ||||||
|  |    label2 = g->cluster[cy2][cx2].clump[c2].global_label; | ||||||
|  |    if (label1.c == label2.c) | ||||||
|  |       return 1; | ||||||
|  |    return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int stbcc_query_grid_open(stbcc_grid *g, int x, int y) | ||||||
|  | { | ||||||
|  |    return STBCC__MAP_OPEN(g, x, y) != 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y) | ||||||
|  | { | ||||||
|  |    stbcc__clumpid c = g->clump_for_node[y][x]; | ||||||
|  |    int cx = STBCC__CLUSTER_X_FOR_COORD_X(x); | ||||||
|  |    int cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y); | ||||||
|  |    return g->cluster[cy][cx].clump[c].global_label.c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    unsigned char x,y; | ||||||
|  | } stbcc__tinypoint; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    stbcc__tinypoint parent[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X]; // 32x32 => 2KB
 | ||||||
|  |    stbcc__clumpid   label[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X]; | ||||||
|  | } stbcc__cluster_build_info; | ||||||
|  | 
 | ||||||
|  | static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy); | ||||||
|  | static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy); | ||||||
|  | static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy); | ||||||
|  | 
 | ||||||
|  | static stbcc__global_clumpid stbcc__clump_find(stbcc_grid *g, stbcc__global_clumpid n) | ||||||
|  | { | ||||||
|  |    stbcc__global_clumpid q; | ||||||
|  |    stbcc__clump *c = &g->cluster[n.f.cluster_y][n.f.cluster_x].clump[n.f.clump_index]; | ||||||
|  | 
 | ||||||
|  |    if (c->global_label.c == n.c) | ||||||
|  |       return n; | ||||||
|  | 
 | ||||||
|  |    q = stbcc__clump_find(g, c->global_label); | ||||||
|  |    c->global_label = q; | ||||||
|  |    return q; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    unsigned int cluster_x; | ||||||
|  |    unsigned int cluster_y; | ||||||
|  |    unsigned int clump_index; | ||||||
|  | } stbcc__unpacked_clumpid; | ||||||
|  | 
 | ||||||
|  | // @OPTIMIZE: pass in these parameters unpacked, not packed
 | ||||||
|  | static void stbcc__clump_union(stbcc_grid *g, stbcc__unpacked_clumpid m, int x, int y, int idx) | ||||||
|  | { | ||||||
|  |    stbcc__clump *mc = &g->cluster[m.cluster_y][m.cluster_x].clump[m.clump_index]; | ||||||
|  |    stbcc__clump *nc = &g->cluster[y][x].clump[idx]; | ||||||
|  |    stbcc__global_clumpid mp = stbcc__clump_find(g, mc->global_label); | ||||||
|  |    stbcc__global_clumpid np = stbcc__clump_find(g, nc->global_label); | ||||||
|  | 
 | ||||||
|  |    if (mp.c == np.c) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    g->cluster[mp.f.cluster_y][mp.f.cluster_x].clump[mp.f.clump_index].global_label = np; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void stbcc__build_connected_components_for_clumps(stbcc_grid *g) | ||||||
|  | { | ||||||
|  |    int i,j,k,h; | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) { | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) { | ||||||
|  |          stbcc__cluster *cluster = &g->cluster[j][i]; | ||||||
|  |          for (k=0; k < (int) cluster->num_clumps; ++k) { | ||||||
|  |             stbcc__global_clumpid m; | ||||||
|  |             m.f.clump_index = k; | ||||||
|  |             m.f.cluster_x = i; | ||||||
|  |             m.f.cluster_y = j; | ||||||
|  |             assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j); | ||||||
|  |             cluster->clump[k].global_label = m; | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) { | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) { | ||||||
|  |          stbcc__cluster *cluster = &g->cluster[j][i]; | ||||||
|  |          for (k=0; k < (int) cluster->num_clumps; ++k) { | ||||||
|  |             stbcc__clump *clump = &cluster->clump[k]; | ||||||
|  |             stbcc__unpacked_clumpid m; | ||||||
|  |             m.clump_index = k; | ||||||
|  |             m.cluster_x = i; | ||||||
|  |             m.cluster_y = j; | ||||||
|  |             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; | ||||||
|  |                stbcc__clump_union(g, m, x, y, clump_index); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) { | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) { | ||||||
|  |          stbcc__cluster *cluster = &g->cluster[j][i]; | ||||||
|  |          for (k=0; k < (int) cluster->num_clumps; ++k) { | ||||||
|  |             stbcc__global_clumpid m; | ||||||
|  |             m.f.clump_index = k; | ||||||
|  |             m.f.cluster_x = i; | ||||||
|  |             m.f.cluster_y = j; | ||||||
|  |             stbcc__clump_find(g, m); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid) | ||||||
|  | { | ||||||
|  |    int cx,cy; | ||||||
|  | 
 | ||||||
|  |    if (!solid) { | ||||||
|  |       if (STBCC__MAP_OPEN(g,x,y)) | ||||||
|  |          return; | ||||||
|  |    } else { | ||||||
|  |       if (!STBCC__MAP_OPEN(g,x,y)) | ||||||
|  |          return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    cx = STBCC__CLUSTER_X_FOR_COORD_X(x); | ||||||
|  |    cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y); | ||||||
|  | 
 | ||||||
|  |    stbcc__remove_connections_to_adjacent_cluster(g, cx-1, cy,  1, 0); | ||||||
|  |    stbcc__remove_connections_to_adjacent_cluster(g, cx+1, cy, -1, 0); | ||||||
|  |    stbcc__remove_connections_to_adjacent_cluster(g, cx, cy-1,  0, 1); | ||||||
|  |    stbcc__remove_connections_to_adjacent_cluster(g, cx, cy+1,  0,-1); | ||||||
|  | 
 | ||||||
|  |    if (!solid) | ||||||
|  |       STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y); | ||||||
|  |    else | ||||||
|  |       STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y); | ||||||
|  | 
 | ||||||
|  |    stbcc__build_clumps_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__build_connected_components_for_clumps(g); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t stbcc_grid_sizeof(void) | ||||||
|  | { | ||||||
|  |    return sizeof(stbcc_grid); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h) | ||||||
|  | { | ||||||
|  |    int i,j,k; | ||||||
|  |    assert(w % STBCC__CLUSTER_SIZE_X == 0); | ||||||
|  |    assert(h % STBCC__CLUSTER_SIZE_Y == 0); | ||||||
|  |    assert(w % 8 == 0); | ||||||
|  | 
 | ||||||
|  |    g->w = w; | ||||||
|  |    g->h = h; | ||||||
|  |    g->cw = w >> STBCC_CLUSTER_SIZE_X_LOG2; | ||||||
|  |    g->ch = h >> STBCC_CLUSTER_SIZE_Y_LOG2; | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < h; ++j) { | ||||||
|  |       for (i=0; i < w; i += 8) { | ||||||
|  |          unsigned char c = 0; | ||||||
|  |          for (k=0; k < 8; ++k) | ||||||
|  |             if (map[j*w + (i+k)] == 0) | ||||||
|  |                c |= (1 << k); | ||||||
|  |          g->map[j][i>>3] = c; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < g->ch; ++j) | ||||||
|  |       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); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    stbcc__build_connected_components_for_clumps(g); | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < g->h; ++j) | ||||||
|  |       for (i=0; i < g->w; ++i) | ||||||
|  |          assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2) | ||||||
|  | { | ||||||
|  |    stbcc__clump *clump; | ||||||
|  | 
 | ||||||
|  |    int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1); | ||||||
|  |    int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1); | ||||||
|  |    int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2); | ||||||
|  |    int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2); | ||||||
|  | 
 | ||||||
|  |    stbcc__clumpid c1 = g->clump_for_node[y1][x1]; | ||||||
|  |    stbcc__clumpid c2 = g->clump_for_node[y2][x2]; | ||||||
|  | 
 | ||||||
|  |    stbcc__relative_clumpid rc; | ||||||
|  | 
 | ||||||
|  |    assert(cx1 != cx2 || cy1 != cy2); | ||||||
|  |    assert(abs(cx1-cx2) + abs(cy1-cy2) == 1); | ||||||
|  | 
 | ||||||
|  |    // add connection to c2 in c1
 | ||||||
|  | 
 | ||||||
|  |    rc.clump_index = c2; | ||||||
|  |    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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2) | ||||||
|  | { | ||||||
|  |    stbcc__clump *clump; | ||||||
|  |    int i; | ||||||
|  | 
 | ||||||
|  |    int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1); | ||||||
|  |    int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1); | ||||||
|  |    int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2); | ||||||
|  |    int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2); | ||||||
|  | 
 | ||||||
|  |    stbcc__clumpid c1 = g->clump_for_node[y1][x1]; | ||||||
|  |    stbcc__clumpid c2 = g->clump_for_node[y2][x2]; | ||||||
|  | 
 | ||||||
|  |    stbcc__relative_clumpid rc; | ||||||
|  | 
 | ||||||
|  |    assert(cx1 != cx2 || cy1 != cy2); | ||||||
|  |    assert(abs(cx1-cx2) + abs(cy1-cy2) == 1); | ||||||
|  | 
 | ||||||
|  |    // add connection to c2 in c1
 | ||||||
|  | 
 | ||||||
|  |    rc.clump_index = c2; | ||||||
|  |    rc.cluster_dx = x2-x1; | ||||||
|  |    rc.cluster_dy = y2-y1; | ||||||
|  | 
 | ||||||
|  |    clump = &g->cluster[cy1][cx1].clump[c1]; | ||||||
|  | 
 | ||||||
|  |    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) 
 | ||||||
|  |          break; | ||||||
|  | 
 | ||||||
|  |    if (i < clump->num_adjacent) | ||||||
|  |       clump->adjacent_clumps[i] = clump->adjacent_clumps[--clump->num_adjacent]; | ||||||
|  |    else | ||||||
|  |       assert(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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 }; | ||||||
|  |    int x = cx * STBCC__CLUSTER_SIZE_X; | ||||||
|  |    int y = cy * STBCC__CLUSTER_SIZE_Y; | ||||||
|  |    int step_x, step_y=0, i, j, k, n; | ||||||
|  | 
 | ||||||
|  |    if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    assert(abs(dx) + abs(dy) == 1); | ||||||
|  | 
 | ||||||
|  |    if (dx == 1) { | ||||||
|  |       i = STBCC__CLUSTER_SIZE_X-1; | ||||||
|  |       j = 0; | ||||||
|  |       step_x = 0; | ||||||
|  |       step_y = 1; | ||||||
|  |       n = STBCC__CLUSTER_SIZE_Y; | ||||||
|  |    } else if (dx == -1) { | ||||||
|  |       i = 0; | ||||||
|  |       j = 0; | ||||||
|  |       step_x = 0; | ||||||
|  |       step_y = 1;  
 | ||||||
|  |       n = STBCC__CLUSTER_SIZE_Y; | ||||||
|  |    } else if (dy == -1) { | ||||||
|  |       i = 0; | ||||||
|  |       j = 0; | ||||||
|  |       step_x = 1; | ||||||
|  |       step_y = 0; | ||||||
|  |       n = STBCC__CLUSTER_SIZE_X; | ||||||
|  |    } else if (dy == 1) { | ||||||
|  |       i = 0; | ||||||
|  |       j = STBCC__CLUSTER_SIZE_Y-1; | ||||||
|  |       step_x = 1; | ||||||
|  |       step_y = 0; | ||||||
|  |       n = STBCC__CLUSTER_SIZE_X; | ||||||
|  |    } else { | ||||||
|  |       assert(0); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    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); | ||||||
|  |             stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |       i += step_x; | ||||||
|  |       j += step_y; | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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 }; | ||||||
|  |    int x = cx * STBCC__CLUSTER_SIZE_X; | ||||||
|  |    int y = cy * STBCC__CLUSTER_SIZE_Y; | ||||||
|  |    int step_x, step_y=0, i, j, k, n; | ||||||
|  | 
 | ||||||
|  |    if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    assert(abs(dx) + abs(dy) == 1); | ||||||
|  | 
 | ||||||
|  |    if (dx == 1) { | ||||||
|  |       i = STBCC__CLUSTER_SIZE_X-1; | ||||||
|  |       j = 0; | ||||||
|  |       step_x = 0; | ||||||
|  |       step_y = 1; | ||||||
|  |       n = STBCC__CLUSTER_SIZE_Y; | ||||||
|  |    } else if (dx == -1) { | ||||||
|  |       i = 0; | ||||||
|  |       j = 0; | ||||||
|  |       step_x = 0; | ||||||
|  |       step_y = 1;  
 | ||||||
|  |       n = STBCC__CLUSTER_SIZE_Y; | ||||||
|  |    } else if (dy == -1) { | ||||||
|  |       i = 0; | ||||||
|  |       j = 0; | ||||||
|  |       step_x = 1; | ||||||
|  |       step_y = 0; | ||||||
|  |       n = STBCC__CLUSTER_SIZE_X; | ||||||
|  |    } else if (dy == 1) { | ||||||
|  |       i = 0; | ||||||
|  |       j = STBCC__CLUSTER_SIZE_Y-1; | ||||||
|  |       step_x = 1; | ||||||
|  |       step_y = 0; | ||||||
|  |       n = STBCC__CLUSTER_SIZE_X; | ||||||
|  |    } else { | ||||||
|  |       assert(0); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    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__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |       i += step_x; | ||||||
|  |       j += step_y; | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static stbcc__tinypoint stbcc__incluster_find(stbcc__cluster_build_info *cbi, int x, int y) | ||||||
|  | { | ||||||
|  |    stbcc__tinypoint p,q; | ||||||
|  |    p = cbi->parent[y][x]; | ||||||
|  |    if (p.x == x && p.y == y) | ||||||
|  |       return p; | ||||||
|  |    q = stbcc__incluster_find(cbi, p.x, p.y); | ||||||
|  |    cbi->parent[y][x] = q; | ||||||
|  |    return q; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void stbcc__incluster_union(stbcc__cluster_build_info *cbi, int x1, int y1, int x2, int y2) | ||||||
|  | { | ||||||
|  |    stbcc__tinypoint p = stbcc__incluster_find(cbi, x1,y1); | ||||||
|  |    stbcc__tinypoint q = stbcc__incluster_find(cbi, x2,y2); | ||||||
|  | 
 | ||||||
|  |    if (p.x == q.x && p.y == q.y) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    cbi->parent[p.y][p.x] = q; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy) | ||||||
|  | { | ||||||
|  |    stbcc__cluster *c; | ||||||
|  |    stbcc__cluster_build_info cbi; | ||||||
|  |    int label=0; | ||||||
|  |    int i,j; | ||||||
|  |    int x = cx * STBCC__CLUSTER_SIZE_X; | ||||||
|  |    int y = cy * STBCC__CLUSTER_SIZE_Y; | ||||||
|  | 
 | ||||||
|  |    // set initial disjoint set forest state
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) { | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) { | ||||||
|  |          cbi.parent[j][i].x = i; | ||||||
|  |          cbi.parent[j][i].y = j; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // join all sets that are connected
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) { | ||||||
|  |       // check down only if not on bottom row
 | ||||||
|  |       if (j < STBCC__CLUSTER_SIZE_Y-1) | ||||||
|  |          for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) | ||||||
|  |             if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i  ,y+j+1)) | ||||||
|  |                stbcc__incluster_union(&cbi, i,j, i,j+1); | ||||||
|  |       // check right for everything but rightmost column
 | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i) | ||||||
|  |          if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j  )) | ||||||
|  |             stbcc__incluster_union(&cbi, i,j, i+1,j); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // label all non-empty leaders
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) { | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) { | ||||||
|  |          stbcc__tinypoint p = cbi.parent[j][i]; | ||||||
|  |          if (p.x == i && p.y == j) | ||||||
|  |             if (STBCC__MAP_OPEN(g,x+i,y+j)) | ||||||
|  |                cbi.label[j][i] = label++; | ||||||
|  |             else | ||||||
|  |                cbi.label[j][i] = STBCC__NULL_CLUMPID; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // label all other nodes
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) { | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) { | ||||||
|  |          stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j); | ||||||
|  |          if (p.x != i || p.y != j) { | ||||||
|  |             if (STBCC__MAP_OPEN(g,x+i,y+j)) | ||||||
|  |                cbi.label[j][i] = cbi.label[p.y][p.x]; | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    c = &g->cluster[cy][cx]; | ||||||
|  |    c->num_clumps = label; | ||||||
|  |    for (i=0; i < label; ++i) | ||||||
|  |       c->clump[i].num_adjacent = 0; | ||||||
|  | 
 | ||||||
|  |    for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) | ||||||
|  |       for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) { | ||||||
|  |          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); | ||||||
|  |       } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // STB_CONNECTED_COMPONENTS_IMPLEMENTATION
 | ||||||
					Loading…
					
					
				
		Reference in New Issue