// backtracks lead to endless loops - must rework the architecture sclass SimpleOutlineWalker is Steppable { IIntIntPred pixelIsInside; int x, y; settable bool processBacktracks = false; int startX, startY; NESWDirection direction = NESWDirection.east; gettable int nPixels; new PtBuffer pixels; new L backtrackStack; // seen points as intPairToLong new CompactLongSet seen; event foundPoint(int x, int y); srecord BacktrackPoint(int x, int y, NESWDirection dir, int turn) {} *(IIntIntPred pixelIsInside, Pt p) { this(pixelIsInside, p.x, p.y); } // x, y must be inside the shape *(IIntIntPred *pixelIsInside, int *x, int *y) { if (!pixelIsInside.get(x, y)) fail("pixel is not inside shape: " + x + "/" + y); // go north until we hit the border while (pixelIsInside.get(x, y-1)) --y; startX = x; startY = y; this.y = y; } public bool step() { ++nPixels; pixels?.add(x, y); foundPoint(x, y); if (walkOneStep()) true; if (!processBacktracks) false; while (nempty(backtrackStack)) if (processBacktrack()) true; false; } bool walkOneStep(int turn default 3) { // try left, straight, right, back for (; turn <= 6; turn++) { NESWDirection newDirection = direction.plus(turn); int x2 = x+newDirection.x, y2 = y+newDirection.y; bool inside = pixelIsInside.get(x2, y2); ifdef SimpleOutlineWalker_debug printVars(+x, +y, +direction, +turn, +newDirection, +x2, +y2, +inside); endifdef if (inside) { if (turn < 6) backtrackStack.add(new BacktrackPoint(x, y, direction, turn+1)); x = x2; y = y2; direction = newDirection; break; } } ret seen.add(intPairToLong(x, y)); //ret x != startX || y != startY; } bool processBacktrack() { if (empty(backtrackStack)) false; var bt = popLast(backtrackStack); x = bt.x; y = bt.y; direction = bt.dir; ret walkOneStep(bt.turn); } Pt currentPt() { ret pt(x, y); } }