public static void AffectConnectedTiles(Loc rectStart, Loc rectSize, LocAction action, LocTest checkBlock, LocTest checkDiagBlock, Loc loc) { if (action == null) { throw new ArgumentNullException(nameof(action)); } if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } if (checkDiagBlock == null) { throw new ArgumentNullException(nameof(checkDiagBlock)); } // create an array to cache which tiles were already traversed bool[][] fillArray = new bool[rectSize.X][]; for (int ii = 0; ii < rectSize.X; ii++) { fillArray[ii] = new bool[rectSize.Y]; } FloodFill( new Rect(rectStart, rectSize), (Loc testLoc) => (checkBlock(testLoc) || fillArray[testLoc.X - rectStart.X][testLoc.Y - rectStart.Y]), (Loc testLoc) => (checkDiagBlock(testLoc) || fillArray[testLoc.X - rectStart.X][testLoc.Y - rectStart.Y]), (Loc actLoc) => { action(actLoc); fillArray[actLoc.X - rectStart.X][actLoc.Y - rectStart.Y] = true; }, loc); }
public static Loc ResizeJustified <T>(ref T[][] array, int width, int height, Dir8 dir, LocAction newLocOp, LocAction newTileOp) { if (newLocOp == null) { throw new ArgumentNullException(nameof(newLocOp)); } if (newTileOp == null) { throw new ArgumentNullException(nameof(newTileOp)); } dir.Separate(out DirH horiz, out DirV vert); Loc offset = Loc.Zero; if (horiz == DirH.None) { offset.X = (width - array.Length) / 2; } else if (horiz == DirH.Left) { offset.X = width - array.Length; } if (vert == DirV.None) { offset.Y = (height - array[0].Length) / 2; } else if (vert == DirV.Up) { offset.Y = height - array[0].Length; } T[][] prevArray = array; array = new T[width][]; for (int ii = 0; ii < width; ii++) { array[ii] = new T[height]; } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (x >= offset.X && x < offset.X + prevArray.Length && y >= offset.Y && y < offset.Y + prevArray[0].Length) { array[x][y] = prevArray[x - offset.X][y - offset.Y]; newLocOp(new Loc(x, y)); } else { newTileOp(new Loc(x, y)); } } } return(offset); }
private static void ScanFill( Rect rect, LocTest checkBlock, LocTest checkDiagBlock, LocAction fillOp, int min, int max, int range_min, int range_max, int y, bool isNext, DirV dir, Stack <ScanLineTile> stack) { if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } if (fillOp == null) { throw new ArgumentNullException(nameof(fillOp)); } // move y down or up int new_y = y + dir.GetLoc().Y; // for diagonal checking: check slightly further int sub = (range_min > rect.Start.X) ? 1 : 0; int add = (range_max < rect.Start.X + rect.Size.X - 1) ? 1 : 0; int line_start = -1; int x = range_min - sub; for (; x <= range_max + add; x++) { bool unblocked = !checkBlock(new Loc(x, new_y)); // check diagonal if applicable if (x < range_min) { unblocked &= !IsDirBlocked(new Loc(range_min, y), DirExt.Combine(DirH.Left, dir), checkBlock, checkDiagBlock); } else if (x > range_max) { unblocked &= !IsDirBlocked(new Loc(range_max, y), DirExt.Combine(DirH.Right, dir), checkBlock, checkDiagBlock); } // skip testing, if testing previous line within previous range bool empty = (isNext || (x <min || x> max)) && unblocked; if (line_start == -1 && empty) { line_start = x; } else if (line_start > -1 && !empty) { stack.Push(new ScanLineTile(new IntRange(line_start, x - 1), new_y, dir, line_start <= range_min, x > range_max)); line_start = -1; } if (line_start > -1) { fillOp(new Loc(x, new_y)); } if (!isNext && x == min) { x = max; } } if (line_start > -1) { stack.Push(new ScanLineTile(new IntRange(line_start, x - 1), new_y, dir, line_start <= range_min, true)); } }
/// <summary> /// Traverses a grid. Does not internally handle the state of traversed/untraversed nodes. /// </summary> /// <param name="rect"></param> /// <param name="checkBlock"></param> /// <param name="checkDiagBlock"></param> /// <param name="fillOp"></param> /// <param name="loc"></param> public static void FloodFill(Rect rect, LocTest checkBlock, LocTest checkDiagBlock, LocAction fillOp, Loc loc) { if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } if (fillOp == null) { throw new ArgumentNullException(nameof(fillOp)); } Stack <ScanLineTile> stack = new Stack <ScanLineTile>(); stack.Push(new ScanLineTile(new IntRange(loc.X, loc.X), loc.Y, DirV.None, true, true)); fillOp(loc); while (stack.Count > 0) { ScanLineTile fillCandidate = stack.Pop(); int rangeMinX = fillCandidate.X.Min; if (fillCandidate.GoLeft) { while (rangeMinX > rect.Start.X && !checkBlock(new Loc(rangeMinX - 1, fillCandidate.Y))) { rangeMinX--; fillOp(new Loc(rangeMinX, fillCandidate.Y)); } } int rangeMaxX = fillCandidate.X.Max; if (fillCandidate.GoRight) { while (rangeMaxX + 1 < rect.Start.X + rect.Size.X && !checkBlock(new Loc(rangeMaxX + 1, fillCandidate.Y))) { rangeMaxX++; fillOp(new Loc(rangeMaxX, fillCandidate.Y)); } } if (fillCandidate.Y < rect.Start.Y + rect.Size.Y - 1) { ScanFill(rect, checkBlock, checkDiagBlock, fillOp, fillCandidate.X.Min, fillCandidate.X.Max, rangeMinX, rangeMaxX, fillCandidate.Y, fillCandidate.Dir != DirV.Up, DirV.Down, stack); } if (fillCandidate.Y > rect.Start.Y) { ScanFill(rect, checkBlock, checkDiagBlock, fillOp, fillCandidate.X.Min, fillCandidate.X.Max, rangeMinX, rangeMaxX, fillCandidate.Y, fillCandidate.Dir != DirV.Down, DirV.Up, stack); } } }
/// <summary> /// Resizes a 2-D array in a certain direction. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="array"></param> /// <param name="width">The new width.</param> /// <param name="height">The new height.</param> /// <param name="dir">The direction to expand/shrink in.</param> /// <param name="newLocOp">The operation to perform on the tile when it is moved.</param> /// <param name="newTileOp">The operation to perform on the tile when it is completely new.</param> /// <returns></returns> public static Loc ResizeJustified <T>(ref T[][] array, int width, int height, Dir8 dir, LocAction newLocOp, LocAction newTileOp) { if (newLocOp == null) { throw new ArgumentNullException(nameof(newLocOp)); } if (newTileOp == null) { throw new ArgumentNullException(nameof(newTileOp)); } Loc offset = GetResizeOffset(array.Length, array[0].Length, width, height, dir); T[][] prevArray = array; array = new T[width][]; for (int ii = 0; ii < width; ii++) { array[ii] = new T[height]; } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (x >= offset.X && x < offset.X + prevArray.Length && y >= offset.Y && y < offset.Y + prevArray[0].Length) { array[x][y] = prevArray[x - offset.X][y - offset.Y]; newLocOp(new Loc(x, y)); } else { newTileOp(new Loc(x, y)); } } } return(offset); }