/* * pushNewPixel() * * Input: lqueue * x, y (pixel coordinates) * &minx, &maxx, &miny, &maxy (<return> bounding box update) * Return: void * * Notes: * (1) This is a wrapper for adding a NewPixel to a queue, which * updates the bounding box for all pixels on that queue and * uses the storage stack to retrieve a NewPixel. */ static void pushNewPixel(L_QUEUE *lq, l_int32 x, l_int32 y, l_int32 *pminx, l_int32 *pmaxx, l_int32 *pminy, l_int32 *pmaxy) { L_NEWPIXEL *np; PROCNAME("pushNewPixel"); if (!lq) { L_ERROR("queue not defined\n", procName); return; } /* Adjust bounding box */ *pminx = L_MIN(*pminx, x); *pmaxx = L_MAX(*pmaxx, x); *pminy = L_MIN(*pminy, y); *pmaxy = L_MAX(*pmaxy, y); /* Get a newpixel to use */ if (lstackGetCount(lq->stack) > 0) np = (L_NEWPIXEL *) lstackRemove(lq->stack); else np = (L_NEWPIXEL *) CALLOC(1, sizeof(L_NEWPIXEL)); np->x = x; np->y = y; lqueueAdd(lq, np); return; }
/*! * pixSearchBinaryMaze() * * Input: pixs (1 bpp, maze) * xi, yi (beginning point; use same initial point * that was used to generate the maze) * xf, yf (end point, or close to it) * &ppixd (<optional return> maze with path illustrated, or * if no path possible, the part of the maze * that was searched) * Return: pta (shortest path), or null if either no path * exists or on error * * Notes: * (1) Because of the overhead in calling pixGetPixel() and * pixSetPixel(), we have used raster line pointers and the * GET_DATA* and SET_DATA* macros for many of the pix accesses. * (2) Commentary: * The goal is to find the shortest path between beginning and * end points, without going through walls, and there are many * ways to solve this problem. * We use a queue to implement a breadth-first search. Two auxiliary * "image" data structures can be used: one to mark the visited * pixels and one to give the direction to the parent for each * visited pixels. The first structure is used to avoid putting * pixels on the queue more than once, and the second is used * for retracing back to the origin, like the breadcrumbs in * Hansel and Gretel. Each pixel taken off the queue is destroyed * after it is used to locate the allowed neighbors. In fact, * only one distance image is required, if you initialize it * to some value that signifies "not yet visited." (We use * a binary image for marking visited pixels because it is clearer.) * This method for a simple search of a binary maze is implemented in * searchBinaryMaze(). * An alternative method would store the (manhattan) distance * from the start point with each pixel on the queue. The children * of each pixel get a distance one larger than the parent. These * values can be stored in an auxiliary distance map image * that is constructed simultaneously with the search. Once the * end point is reached, the distance map is used to backtrack * along a minimum path. There may be several equal length * minimum paths, any one of which can be chosen this way. */ PTA * pixSearchBinaryMaze(PIX *pixs, l_int32 xi, l_int32 yi, l_int32 xf, l_int32 yf, PIX **ppixd) { l_int32 i, j, x, y, w, h, d, found; l_uint32 val, rpixel, gpixel, bpixel; void **lines1, **linem1, **linep8, **lined32; MAZEEL *el, *elp; PIX *pixd; /* the shortest path written on the maze image */ PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ PIX *pixp; /* for bookkeeping, to indicate direction to parent */ L_QUEUE *lq; PTA *pta; PROCNAME("pixSearchBinaryMaze"); if (ppixd) *ppixd = NULL; if (!pixs) return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); if (xi <= 0 || xi >= w) return (PTA *)ERROR_PTR("xi not valid", procName, NULL); if (yi <= 0 || yi >= h) return (PTA *)ERROR_PTR("yi not valid", procName, NULL); pixGetPixel(pixs, xi, yi, &val); if (val != 0) return (PTA *)ERROR_PTR("(xi,yi) not bg pixel", procName, NULL); pixd = NULL; pta = NULL; /* Find a bg pixel near input point (xf, yf) */ localSearchForBackground(pixs, &xf, &yf, 5); #if DEBUG_MAZE fprintf(stderr, "(xi, yi) = (%d, %d), (xf, yf) = (%d, %d)\n", xi, yi, xf, yf); #endif /* DEBUG_MAZE */ pixm = pixCreate(w, h, 1); /* initialized to OFF */ pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ lines1 = pixGetLinePtrs(pixs, NULL); linem1 = pixGetLinePtrs(pixm, NULL); linep8 = pixGetLinePtrs(pixp, NULL); lq = lqueueCreate(0); /* Prime the queue with the first pixel; it is OFF */ el = mazeelCreate(xi, yi, 0); /* don't need direction here */ pixSetPixel(pixm, xi, yi, 1); /* mark visited */ lqueueAdd(lq, el); /* Fill up the pix storing directions to parents, * stopping when we hit the point (xf, yf) */ found = FALSE; while (lqueueGetCount(lq) > 0) { elp = (MAZEEL *)lqueueRemove(lq); x = elp->x; y = elp->y; if (x == xf && y == yf) { found = TRUE; FREE(elp); break; } if (x > 0) { /* check to west */ val = GET_DATA_BIT(linem1[y], x - 1); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y], x - 1); /* mark visited */ val = GET_DATA_BIT(lines1[y], x - 1); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent E */ el = mazeelCreate(x - 1, y, 0); lqueueAdd(lq, el); } } } if (y > 0) { /* check north */ val = GET_DATA_BIT(linem1[y - 1], x); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y - 1], x); /* mark visited */ val = GET_DATA_BIT(lines1[y - 1], x); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent S */ el = mazeelCreate(x, y - 1, 0); lqueueAdd(lq, el); } } } if (x < w - 1) { /* check east */ val = GET_DATA_BIT(linem1[y], x + 1); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y], x + 1); /* mark visited */ val = GET_DATA_BIT(lines1[y], x + 1); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent W */ el = mazeelCreate(x + 1, y, 0); lqueueAdd(lq, el); } } } if (y < h - 1) { /* check south */ val = GET_DATA_BIT(linem1[y + 1], x); if (val == 0) { /* not yet visited */ SET_DATA_BIT(linem1[y + 1], x); /* mark visited */ val = GET_DATA_BIT(lines1[y + 1], x); if (val == 0) { /* bg, not a wall */ SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent N */ el = mazeelCreate(x, y + 1, 0); lqueueAdd(lq, el); } } } FREE(elp); } lqueueDestroy(&lq, TRUE); pixDestroy(&pixm); FREE(linem1); if (ppixd) { pixd = pixUnpackBinary(pixs, 32, 1); *ppixd = pixd; } composeRGBPixel(255, 0, 0, &rpixel); /* start point */ composeRGBPixel(0, 255, 0, &gpixel); composeRGBPixel(0, 0, 255, &bpixel); /* end point */ if (!found) { L_INFO(" No path found", procName); if (pixd) { /* paint all visited locations */ lined32 = pixGetLinePtrs(pixd, NULL); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { val = GET_DATA_BYTE(linep8[i], j); if (val != 0 && pixd) SET_DATA_FOUR_BYTES(lined32[i], j, gpixel); } } FREE(lined32); } } else { /* write path onto pixd */ L_INFO(" Path found", procName); pta = ptaCreate(0); x = xf; y = yf; while (1) { ptaAddPt(pta, x, y); if (x == xi && y == yi) break; if (pixd) pixSetPixel(pixd, x, y, gpixel); pixGetPixel(pixp, x, y, &val); if (val == DIR_NORTH) y--; else if (val == DIR_SOUTH) y++; else if (val == DIR_EAST) x++; else if (val == DIR_WEST) x--; } } if (pixd) { pixSetPixel(pixd, xi, yi, rpixel); pixSetPixel(pixd, xf, yf, bpixel); } pixDestroy(&pixp); FREE(lines1); FREE(linep8); return pta; }
/*! * generateBinaryMaze() * * Input: w, h (size of maze) * xi, yi (initial location) * wallps (probability that a pixel to the side is ON) * ranis (ratio of prob that pixel in forward direction * is a wall to the probability that pixel in * side directions is a wall) * Return: pix, or null on error * * Notes: * (1) We have two input probability factors that determine the * density of walls and average length of straight passages. * When ranis < 1.0, you are more likely to generate a wall * to the side than going forward. Enter 0.0 for either if * you want to use the default values. * (2) This is a type of percolation problem, and exhibits * different phases for different parameters wallps and ranis. * For larger values of these parameters, regions in the maze * are not explored because the maze generator walls them * off and cannot get through. The boundary between the * two phases in this two-dimensional parameter space goes * near these values: * wallps ranis * 0.35 1.00 * 0.40 0.85 * 0.45 0.70 * 0.50 0.50 * 0.55 0.40 * 0.60 0.30 * 0.65 0.25 * 0.70 0.19 * 0.75 0.15 * 0.80 0.11 * (3) Because here is a considerable amount of overhead in calling * pixGetPixel() and pixSetPixel(), this function can be sped * up with little effort using raster line pointers and the * GET_DATA* and SET_DATA* macros. */ PIX * generateBinaryMaze(l_int32 w, l_int32 h, l_int32 xi, l_int32 yi, l_float32 wallps, l_float32 ranis) { l_int32 x, y, dir; l_uint32 val; l_float32 frand, wallpf, testp; MAZEEL *el, *elp; PIX *pixd; /* the destination maze */ PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ L_QUEUE *lq; /* On Windows, seeding is apparently necessary to get decent mazes. * Windows rand() returns a value up to 2^15 - 1, whereas unix * rand() returns a value up to 2^31 - 1. Therefore the generated * mazes will differ on the two platforms. */ #ifdef _WIN32 srand(28*333); #endif /* _WIN32 */ if (w < MIN_MAZE_WIDTH) w = MIN_MAZE_WIDTH; if (h < MIN_MAZE_HEIGHT) h = MIN_MAZE_HEIGHT; if (xi <= 0 || xi >= w) xi = w / 6; if (yi <= 0 || yi >= h) yi = h / 5; if (wallps < 0.05 || wallps > 0.95) wallps = DEFAULT_WALL_PROBABILITY; if (ranis < 0.05 || ranis > 1.0) ranis = DEFAULT_ANISOTROPY_RATIO; wallpf = wallps * ranis; #if DEBUG_MAZE fprintf(stderr, "(w, h) = (%d, %d), (xi, yi) = (%d, %d)\n", w, h, xi, yi); fprintf(stderr, "Using: prob(wall) = %7.4f, anisotropy factor = %7.4f\n", wallps, ranis); #endif /* DEBUG_MAZE */ /* These are initialized to OFF */ pixd = pixCreate(w, h, 1); pixm = pixCreate(w, h, 1); lq = lqueueCreate(0); /* Prime the queue with the first pixel; it is OFF */ el = mazeelCreate(xi, yi, START_LOC); pixSetPixel(pixm, xi, yi, 1); /* mark visited */ lqueueAdd(lq, el); /* While we're at it ... */ while (lqueueGetCount(lq) > 0) { elp = (MAZEEL *)lqueueRemove(lq); x = elp->x; y = elp->y; dir = elp->dir; if (x > 0) { /* check west */ pixGetPixel(pixm, x - 1, y, &val); if (val == 0) { /* not yet visited */ pixSetPixel(pixm, x - 1, y, 1); /* mark visited */ frand = (l_float32)rand() / (l_float32)RAND_MAX; testp = wallps; if (dir == DIR_WEST) testp = wallpf; if (frand <= testp) { /* make it a wall */ pixSetPixel(pixd, x - 1, y, 1); } else { /* not a wall */ el = mazeelCreate(x - 1, y, DIR_WEST); lqueueAdd(lq, el); } } } if (y > 0) { /* check north */ pixGetPixel(pixm, x, y - 1, &val); if (val == 0) { /* not yet visited */ pixSetPixel(pixm, x, y - 1, 1); /* mark visited */ frand = (l_float32)rand() / (l_float32)RAND_MAX; testp = wallps; if (dir == DIR_NORTH) testp = wallpf; if (frand <= testp) { /* make it a wall */ pixSetPixel(pixd, x, y - 1, 1); } else { /* not a wall */ el = mazeelCreate(x, y - 1, DIR_NORTH); lqueueAdd(lq, el); } } } if (x < w - 1) { /* check east */ pixGetPixel(pixm, x + 1, y, &val); if (val == 0) { /* not yet visited */ pixSetPixel(pixm, x + 1, y, 1); /* mark visited */ frand = (l_float32)rand() / (l_float32)RAND_MAX; testp = wallps; if (dir == DIR_EAST) testp = wallpf; if (frand <= testp) { /* make it a wall */ pixSetPixel(pixd, x + 1, y, 1); } else { /* not a wall */ el = mazeelCreate(x + 1, y, DIR_EAST); lqueueAdd(lq, el); } } } if (y < h - 1) { /* check south */ pixGetPixel(pixm, x, y + 1, &val); if (val == 0) { /* not yet visited */ pixSetPixel(pixm, x, y + 1, 1); /* mark visited */ frand = (l_float32)rand() / (l_float32)RAND_MAX; testp = wallps; if (dir == DIR_SOUTH) testp = wallpf; if (frand <= testp) { /* make it a wall */ pixSetPixel(pixd, x, y + 1, 1); } else { /* not a wall */ el = mazeelCreate(x, y + 1, DIR_SOUTH); lqueueAdd(lq, el); } } } FREE(elp); } lqueueDestroy(&lq, TRUE); pixDestroy(&pixm); return pixd; }