srecord noeq G22RegionThinner_v2(IImageRegion originalRegion) is Steppable {
Rect bounds;
IImageRegion region;
// 0 = outside of region
// 1 = inner pixel
// 2 = border pixel
// index is in bounds coordinates
byte[] pixels;
int idx(int x, int y) {
ret (y-bounds.y)*bounds.w+x-bounds.x;
}
int idx(Pt p) { ret idx(p.x, p.y); }
Pt idxToPt(int idx) {
ret pt(bounds.x+(idx % bounds.w), bounds.y+idx/bounds.w);
}
byte getPixel(Pt p) {
ret !containsPt(bounds, p) ? 0 : pixels[idx(p)];
}
void init {
if (bounds != null) ret;
bounds = originalRegion.bounds();
pixels = new byte[area(bounds)];
for (Pt p : originalRegion.pixelIterator())
pixels[idx(p.x, p.y)] = 1;
region = new ThinnedRegion;
}
class ThinnedRegion is IImageRegion {
public Img image() { ret originalRegion.image(); }
public Rect bounds() { ret bounds; }
public bool contains(int x, int y) {
ret containsPt(bounds, x, y) && pixels[idx(x, y)] > 0;
}
public ItIt pixelIterator() {
ret iff_null(new IF0 {
int idx = 0;
public Pt get() {
for (; idx < pixels.length; idx++)
if (pixels[idx] > 0)
ret idxToPt(idx++);
null;
}
});
}
}
public bool step() {
init();
L traces = g22_allBorderTraces_withDiagonals(region);
for (points : traces)
for (p : points)
pixels[idx(p)] = 2;
new PtBuffer toDelete;
for (points : traces)
for ping (p : points) {
if (deletableBorderPoint(p))
toDelete.add(p);
}
for (p : toDelete)
pixels[idx(p)] = 0;
ret nempty(toDelete);
}
bool deletableBorderPoint(Pt p) {
int surroundingBorderPixels = 0, surroundingInnerPixels = 0;
for (int dir = 1; dir <= 8; dir++) {
Pt p2 = ptPlus(p, onePathDirection(dir));
byte value = getPixel(p2);
if (value == 2)
surroundingBorderPixels++;
else if (value == 1)
surroundingInnerPixels++;
}
bool deletable = surroundingInnerPixels > 0 && surroundingBorderPixels <= 2;
printVars ifdef G22RegionThinner_debug(+p, +surroundingInnerPixels, +surroundingBorderPixels, +deletable);
ret deletable;
}
IImageRegion region aka get() { ret or(region, originalRegion); }
}