Fix grid generation crashes at Penrose 3x3 sizes. What seemed to be

happening was that at the point of calling grid_make_consistent, the
grid had no faces or vertices, probably because grid_trim_vigorously
had removed them all, causing grid_make_consistent to try to allocate
a negative amount of memory and die in snewn.

Fixed by detecting this case in new_desc_penrose and retrying until
generation is successful. (It wasn't happening 100% of the time, just
_most_ of the time.) The same verification step is also used in
validate_desc_penrose in case a user manages to manually construct a
set of parameters leading to this failure mode.

[originally from svn r9840]
This commit is contained in:
Simon Tatham
2013-05-10 17:56:00 +00:00
parent e5df38fa15
commit b6ee9debcf

53
grid.c
View File

@ -2631,6 +2631,8 @@ static void grid_size_penrose(int width, int height,
*yextent = l * height; *yextent = l * height;
} }
static grid *grid_new_penrose(int width, int height, int which, const char *desc); /* forward reference */
static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs) static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs)
{ {
int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff; int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff;
@ -2638,10 +2640,13 @@ static char *grid_new_desc_penrose(grid_type type, int width, int height, random
int inner_radius; int inner_radius;
char gd[255]; char gd[255];
int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3); int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
grid *g;
/* We want to produce a random bit of penrose tiling, so we calculate while (1) {
* a random offset (within the patch that penrose.c calculates for us) /* We want to produce a random bit of penrose tiling, so we
* and an angle (multiple of 36) to rotate the patch. */ * calculate a random offset (within the patch that penrose.c
* calculates for us) and an angle (multiple of 36) to rotate
* the patch. */
penrose_calculate_size(which, tilesize, width, height, penrose_calculate_size(which, tilesize, width, height,
&outer_radius, &startsz, &depth); &outer_radius, &startsz, &depth);
@ -2650,8 +2655,8 @@ static char *grid_new_desc_penrose(grid_type type, int width, int height, random
* radius calculated. */ * radius calculated. */
inner_radius = (int)(outer_radius - sqrt(width*width + height*height)); inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
/* Pick a random offset (the easy way: choose within outer square, /* Pick a random offset (the easy way: choose within outer
* discarding while it's outside the circle) */ * square, discarding while it's outside the circle) */
do { do {
xoff = random_upto(rs, 2*inner_radius) - inner_radius; xoff = random_upto(rs, 2*inner_radius) - inner_radius;
yoff = random_upto(rs, 2*inner_radius) - inner_radius; yoff = random_upto(rs, 2*inner_radius) - inner_radius;
@ -2665,6 +2670,19 @@ static char *grid_new_desc_penrose(grid_type type, int width, int height, random
sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff); sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
/*
* Now test-generate our grid, to make sure it actually
* produces something.
*/
g = grid_new_penrose(width, height, which, gd);
if (g) {
grid_free(g);
break;
}
/* If not, go back to the top of this while loop and try again
* with a different random offset. */
}
return dupstr(gd); return dupstr(gd);
} }
@ -2674,6 +2692,7 @@ static char *grid_validate_desc_penrose(grid_type type, int width, int height,
int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius; int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius;
double outer_radius; double outer_radius;
int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3); int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
grid *g;
if (!desc) if (!desc)
return "Missing grid description string."; return "Missing grid description string.";
@ -2690,6 +2709,15 @@ static char *grid_validate_desc_penrose(grid_type type, int width, int height,
if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360) if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360)
return "Angle offset out of bounds."; return "Angle offset out of bounds.";
/*
* Test-generate to ensure these parameters don't end us up with
* no grid at all.
*/
g = grid_new_penrose(width, height, which, desc);
if (!g)
return "Patch coordinates do not identify a usable grid fragment";
grid_free(g);
return NULL; return NULL;
} }
@ -2763,7 +2791,22 @@ static grid *grid_new_penrose(int width, int height, int which, const char *desc
debug(("penrose: %d faces total (equivalent to %d wide by %d high)", debug(("penrose: %d faces total (equivalent to %d wide by %d high)",
g->num_faces, g->num_faces/height, g->num_faces/width)); g->num_faces, g->num_faces/height, g->num_faces/width));
/*
* Return NULL if we ended up with an empty grid, either because
* the initial generation was over too small a rectangle to
* encompass any face or because grid_trim_vigorously ended up
* removing absolutely everything.
*/
if (g->num_faces == 0 || g->num_dots == 0) {
grid_free(g);
return NULL;
}
grid_trim_vigorously(g); grid_trim_vigorously(g);
if (g->num_faces == 0 || g->num_dots == 0) {
grid_free(g);
return NULL;
}
grid_make_consistent(g); grid_make_consistent(g);
/* /*