public void Crawl(Point startingPoint, Color targetColor, Color newColor) { if (ColorComparer.AreAlike(targetColor, newColor)) throw new ArgumentException(nameof(targetColor) + " can't be alike " + nameof(newColor)); Scanlines.Clear(); // Initialize the starting point if (IsValid(startingPoint.X, startingPoint.Y, targetColor)) MarkAsInvalid(startingPoint.X, startingPoint.Y, newColor); else return; // Adds the first scanlineData to the stack var firstLine = new ScanlineData(left: startingPoint.X, right: startingPoint.X, y: startingPoint.Y, directionTheCrawlerIsGoing: ScanlineData.Direction.None, shouldBeExtendedLeft: true, shouldBeExtendedRight: true); Scanlines.Push(firstLine); while (Scanlines.Count > 0) { var currentLine = Scanlines.Pop(); if (currentLine.ShouldBeExtendedLeft) currentLine = CrawLeft(currentLine, targetColor, newColor); if (currentLine.ShouldBeExtendedRight) currentLine = CrawRight(currentLine, targetColor, newColor); // Bitmap's minimum y value is known to be 0 if (currentLine.Y >= 0) CheckAbove(currentLine, targetColor, newColor); //if (CurrentY < MaximumY - 1) // CheckBelow(); } }
private void CheckAbove(ScanlineData currentData, Color targetColor, Color newColor) { throw new NotImplementedException(); /* int localHeight = CurrentY - 1; var nextData = new ScanlineData(ExtendedLeft, ExtendedLeft, localHeight, ScanlineData.Direction.Up, false, false); bool canSkipMiddleOfTheLine = CurrentLine.DirectionTheCrawlerIsGoing == ScanlineData.Direction.Down; bool dataIsValid = false; bool pushedFirstLine = false; // Checking for trivial cases if (canSkipMiddleOfTheLine && ExtendedLeft == ExtendedRight) return; int x = ExtendedLeft; int y = localHeight; for (int movingX = ExtendedLeft; movingX <= ExtendedRight; movingX++) { x = movingX; if (IsValid(x, y)) { MarkAsInvalid(x, y); if (!dataIsValid) { dataIsValid = true; nextData.Left = movingX; } nextData.Right = movingX; } else { if (dataIsValid) { if (!pushedFirstLine) { nextData.ShouldBeExtendedLeft = nextData.Left == ExtendedLeft; ranges.Push(nextData); pushedFirstLine = true; } else { ranges.Push(nextData); } dataIsValid = false; nextData = new ScanlineData(movingX, movingX, localHeight, ScanlineData.Direction.Up, false, false); } } if (movingX == CurrentLine.Left && canSkipMiddleOfTheLine) { // Skip previously-checked points if (dataIsValid) { ranges.Push(nextData); //nextData = new ScanlineData(movingX, movingX, localHeight, ScanlineData.Direction.Up, false, false); nextData = new ScanlineData(CurrentLine.Right, CurrentLine.Right, localHeight, ScanlineData.Direction.Up, false, false); } dataIsValid = false; movingX = CurrentLine.Right; } } if (dataIsValid && nextData.Right == ExtendedRight) { nextData.ShouldBeExtendedLeft = (nextData.Left == ExtendedLeft); nextData.ShouldBeExtendedRight = true; ranges.Push(nextData); } */ }
private ScanlineData CrawRight(ScanlineData currentData, Color targetColor, Color newColor) { int x = currentData.Right; int maximumX = Bitmap.Width - 1; // The idea is to find the biggest x value for the current y // that still makes IsValid(...) return true while (x <= maximumX) { x++; if (IsValid(x, currentData.Y, targetColor)) { MarkAsInvalid(x, currentData.Y, newColor); } else { // This means increased x one too many times, oughta undo it and stop trying x--; break; } } var newScanline = new ScanlineData(left: currentData.Left, right: x, y: currentData.Y, directionTheCrawlerIsGoing: currentData.DirectionTheCrawlerIsGoing, shouldBeExtendedLeft: currentData.ShouldBeExtendedLeft, shouldBeExtendedRight: false); return newScanline; }
private ScanlineData CrawLeft(ScanlineData currentData, Color targetColor, Color newColor) { int x = currentData.Left; // Bitmap's minimum x value is known to be 0 int minimumX = 0; // The idea is to find the smallest x value for the current y // that still makes IsValid(...) return true while (x >= minimumX) { x--; if (IsValid(x, currentData.Y, targetColor)) { MarkAsInvalid(x, currentData.Y, newColor); } else { // This means decreased x one too many times, oughta undo it and stop trying x++; break; } } var newScanline = new ScanlineData(left: x, right: currentData.Right, y: currentData.Y, directionTheCrawlerIsGoing: currentData.DirectionTheCrawlerIsGoing, shouldBeExtendedLeft: false, shouldBeExtendedRight: currentData.ShouldBeExtendedRight); return newScanline; }
/// <summary> /// Checks the points directly below the current line. MarkAsInvalid the valid ones and, if necessary, creates new ScanLineDatas to be extended.checked. /// </summary> protected void CheckBelow() { int localHeight = CurrentY + 1; var nextData = new ScanlineData(ExtendedLeft, ExtendedLeft, localHeight, ScanlineData.Direction.Down, false, false); bool canSkipMiddleOfTheLine = CurrentLine.DirectionTheCrawlerIsGoing == ScanlineData.Direction.Up; bool dataIsValid = false; bool pushedFirstLine = false; // Checking for trivial cases if (canSkipMiddleOfTheLine && ExtendedLeft == ExtendedRight) return; int x = ExtendedLeft; int y = localHeight; for (int movingX = ExtendedLeft; movingX <= ExtendedRight; movingX++) { x = movingX; if (IsValid(x, y)) { MarkAsInvalid(x, y); if (!dataIsValid) { dataIsValid = true; nextData.Left = movingX; } nextData.Right = movingX; } else { if (dataIsValid) { if (!pushedFirstLine) { nextData.ShouldBeExtendedLeft = nextData.Left == ExtendedLeft; ranges.Push(nextData); pushedFirstLine = true; } else { ranges.Push(nextData); } dataIsValid = false; nextData = new ScanlineData(movingX, movingX, localHeight, ScanlineData.Direction.Down, false, false); } } if (movingX == CurrentLine.Left && canSkipMiddleOfTheLine) { // Skip previously-checked points if (dataIsValid) { ranges.Push(nextData); nextData = new ScanlineData(movingX, movingX, localHeight, ScanlineData.Direction.Down, false, false); } dataIsValid = false; movingX = CurrentLine.Right; } } if (dataIsValid && nextData.Right == ExtendedRight) { nextData.ShouldBeExtendedLeft = (nextData.Left == ExtendedLeft); nextData.ShouldBeExtendedRight = true; ranges.Push(nextData); } }
/// <summary> /// Starts crawling. The crawler will check all points that are connected to this one and mark them as already visited as it goes. /// Stops when there are no more points connected to the initial one. /// </summary> /// <param name="startingX">Ininitial x coordinate.</param> /// <param name="startingY">Ininitial y coordinate.</param> /// <param name="minimumX">Minimum including value of x.</param> /// <param name="minimumY">Minimum including value of Y.</param> /// <param name="maximumX">Maximum non-including value of x.</param> /// <param name="maximumY">Maximum non-including value of y.</param> protected void Crawl(int startingX, int startingY, int minimumX, int minimumY, int maximumX, int maximumY) { MinimumY = minimumY; MaximumY = maximumY; MinimumX = minimumX; MaximumX = maximumX; PreCrawlMethods(); if (IsValid(startingX, startingY)) MarkAsInvalid(startingX, startingY); else return; // "Crawls" the startingPoint ranges.Push(new ScanlineData(startingX, startingX, startingY, ScanlineData.Direction.None, true, true)); while (ranges.Count > 0) { CurrentLine = ranges.Pop(); ExtendedLeft = CurrentLine.Left; ExtendedRight = CurrentLine.Right; CurrentY = CurrentLine.Y; if (CurrentLine.ShouldBeExtendedLeft) ExtendLeft(); if (CurrentLine.ShouldBeExtendedRight) ExtendRight(); /*(if (currentY > ValidArea.Y) CheckAbove(); */ if (CurrentY > MinimumY) CheckAbove(); /*if (currentY < ValidArea.Bottom - 1) CheckBelow(); */ if (CurrentY < MaximumY - 1) CheckBelow(); } PostCrawlMethods(); }