Browse Source

Improved magic wand

flabbet 2 years ago
parent
commit
69682f9af4

+ 33 - 17
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs

@@ -281,21 +281,20 @@ public static class FloodFillHelper
             if(processedPositions.Contains(globalPos))
             if(processedPositions.Contains(globalPos))
                 continue;
                 continue;
 
 
+            visualizer.CurrentContext = $"FloodFill_{chunkPos}";
             var maybeArray = GetChunkFloodFill(
             var maybeArray = GetChunkFloodFill(
                 reallyReferenceChunk,
                 reallyReferenceChunk,
                 chunkSize,
                 chunkSize,
                 chunkPos * chunkSize,
                 chunkPos * chunkSize,
                 document.Size,
                 document.Size,
                 posOnChunk,
                 posOnChunk,
-                colorRange, lines);
+                colorRange, lines, processedPositions);
             
             
-
-            processedPositions.Add(globalPos);
             if (maybeArray is null)
             if (maybeArray is null)
                 continue;
                 continue;
             for (int i = 0; i < chunkSize; i++)
             for (int i = 0; i < chunkSize; i++)
             {
             {
-                if (chunkPos.Y > 0 && maybeArray[i] == Visited) // Top
+                if (chunkPos.Y > 0 && maybeArray[i] == Visited) //Top
                     positionsToFloodFill.Push((new(chunkPos.X, chunkPos.Y - 1), new(i, chunkSize - 1)));
                     positionsToFloodFill.Push((new(chunkPos.X, chunkPos.Y - 1), new(i, chunkSize - 1)));
                 if (chunkPos.Y < imageSizeInChunks.Y - 1 && maybeArray[chunkSize * (chunkSize - 1) + i] == Visited) // Bottom
                 if (chunkPos.Y < imageSizeInChunks.Y - 1 && maybeArray[chunkSize * (chunkSize - 1) + i] == Visited) // Bottom
                     positionsToFloodFill.Push((new(chunkPos.X, chunkPos.Y + 1), new(i, 0)));
                     positionsToFloodFill.Push((new(chunkPos.X, chunkPos.Y + 1), new(i, 0)));
@@ -321,6 +320,8 @@ public static class FloodFillHelper
     {
     {
         bool isEdgeChunk = chunkPos.X == 0 || chunkPos.Y == 0 || chunkPos.X == imageSizeInChunks.X - 1 ||
         bool isEdgeChunk = chunkPos.X == 0 || chunkPos.Y == 0 || chunkPos.X == imageSizeInChunks.X - 1 ||
                            chunkPos.Y == imageSizeInChunks.Y - 1;
                            chunkPos.Y == imageSizeInChunks.Y - 1;
+        
+        visualizer.CurrentContext = "EmptyChunk";
         if (isEdgeChunk)
         if (isEdgeChunk)
         {
         {
             bool isTopEdge = chunkPos.Y == 0;
             bool isTopEdge = chunkPos.Y == 0;
@@ -400,7 +401,6 @@ public static class FloodFillHelper
             i--;
             i--;
         }
         }
         
         
-        selection.MoveTo(startingLine.End);
         selection.Close();
         selection.Close();
         return selection;
         return selection;
     }
     }
@@ -411,9 +411,13 @@ public static class FloodFillHelper
         VecI chunkOffset,
         VecI chunkOffset,
         VecI documentSize,
         VecI documentSize,
         VecI pos,
         VecI pos,
-        ColorBounds bounds, Lines lines)
+        ColorBounds bounds, Lines lines, HashSet<VecI> processedPositions)
     {
     {
-        if (!bounds.IsWithinBounds(referenceChunk.Surface.GetSRGBPixel(pos))) return null;
+        if (!bounds.IsWithinBounds(referenceChunk.Surface.GetSRGBPixel(pos)))
+        {
+            return null;
+        }
+        
         byte[] pixelStates = new byte[chunkSize * chunkSize];
         byte[] pixelStates = new byte[chunkSize * chunkSize];
 
 
         using var refPixmap = referenceChunk.Surface.DrawingSurface.PeekPixels();
         using var refPixmap = referenceChunk.Surface.DrawingSurface.PeekPixels();
@@ -429,21 +433,34 @@ public static class FloodFillHelper
             int pixelOffset = curPos.X + curPos.Y * chunkSize;
             int pixelOffset = curPos.X + curPos.Y * chunkSize;
             VecI globalPos = curPos + chunkOffset;
             VecI globalPos = curPos + chunkOffset;
             Half* refPixel = refArray + pixelOffset * 4;
             Half* refPixel = refArray + pixelOffset * 4;
-            
-            if(!bounds.IsWithinBounds(refPixel)) continue;
+
+            if (!bounds.IsWithinBounds(refPixel))
+            {
+                processedPositions.Add(globalPos);
+                continue;
+            }
             
             
             pixelStates[pixelOffset] = Visited;
             pixelStates[pixelOffset] = Visited;
             
             
-            AddCornerLines(documentSize, chunkOffset, lines, curPos, chunkSize);
-            AddFillContourLines(chunkSize, chunkOffset, bounds, lines, curPos, pixelStates, pixelOffset, refPixel, toVisit,  globalPos, documentSize);
+            visualizer.CurrentContext = "AddCornerLines";
+            AddCornerLines(documentSize, chunkOffset, lines, curPos, chunkSize, processedPositions);
+            
+            visualizer.CurrentContext = "AddFillContourLines";
+            AddFillContourLines(chunkSize, chunkOffset, bounds, lines, curPos, pixelStates, pixelOffset, refPixel, toVisit, globalPos, documentSize, processedPositions);
+            
+            processedPositions.Add(globalPos);
         }
         }
         
         
         return pixelStates;
         return pixelStates;
     }
     }
 
 
     private static unsafe void AddFillContourLines(int chunkSize, VecI chunkOffset, ColorBounds bounds, Lines lines,
     private static unsafe void AddFillContourLines(int chunkSize, VecI chunkOffset, ColorBounds bounds, Lines lines,
-        VecI curPos, byte[] pixelStates, int pixelOffset, Half* refPixel, Stack<VecI> toVisit, VecI globalPos, VecI documentSize)
+        VecI curPos, byte[] pixelStates, int pixelOffset, Half* refPixel, Stack<VecI> toVisit, VecI globalPos,
+        VecI documentSize, HashSet<VecI> processedPositions)
     {
     {
+        
+        if(processedPositions.Contains(globalPos)) return;
+        
         // Left pixel
         // Left pixel
         if (curPos.X > 0 && pixelStates[pixelOffset - 1] != Visited)
         if (curPos.X > 0 && pixelStates[pixelOffset - 1] != Visited)
         {
         {
@@ -509,7 +526,7 @@ public static class FloodFillHelper
         }
         }
     }
     }
 
 
-    private static void AddCornerLines(VecI documentSize, VecI chunkOffset, Lines lines, VecI curPos, int chunkSize)
+    private static void AddCornerLines(VecI documentSize, VecI chunkOffset, Lines lines, VecI curPos, int chunkSize, HashSet<VecI> processedPositions)
     {
     {
         VecI clampedPos = new(
         VecI clampedPos = new(
             Math.Clamp(curPos.X, 0, documentSize.X - 1),
             Math.Clamp(curPos.X, 0, documentSize.X - 1),
@@ -556,21 +573,20 @@ public static class FloodFillHelper
         
         
         if (lines.TryCancelLine(line, direction))
         if (lines.TryCancelLine(line, direction))
         {
         {
+            visualizer.Steps.Add(new Step(line, StepType.CancelLine));
             return;
             return;
         }
         }
-        
-        if(lines.LineDict.Any(x => x.Value.ContainsValue(line))) return;
 
 
         if (calculatedDir == direction)
         if (calculatedDir == direction)
         {
         {
             lines.LineDict[direction][line.Start] = line;
             lines.LineDict[direction][line.Start] = line;
-            visualizer.Steps.Add(line);
+            visualizer.Steps.Add(new Step(line));
         }
         }
         else if(calculatedDir == -direction)
         else if(calculatedDir == -direction)
         {
         {
             Line fixedLine = new Line(line.End, line.Start);
             Line fixedLine = new Line(line.End, line.Start);
             lines.LineDict[direction][line.End] = fixedLine;
             lines.LineDict[direction][line.End] = fixedLine;
-            visualizer.Steps.Add(fixedLine);
+            visualizer.Steps.Add(new Step(fixedLine));
         }
         }
         else
         else
         {
         {

+ 36 - 20
src/PixiEditor.ChangeableDocument/Debugging/MagicWandVisualizer.cs

@@ -9,10 +9,11 @@ namespace PixiEditor.ChangeableDocument.Debugging;
 
 
 public class MagicWandVisualizer
 public class MagicWandVisualizer
 {
 {
+    public string CurrentContext { get; set; } = "";
     public string FilePath { get; }
     public string FilePath { get; }
     private Paint drawingPaint;
     private Paint drawingPaint;
     private Paint replacementPaint;
     private Paint replacementPaint;
-    public List<FloodFillHelper.Line> Steps = new List<FloodFillHelper.Line>();
+    public List<Step> Steps = new List<Step>();
     
     
     public MagicWandVisualizer(string filePath)
     public MagicWandVisualizer(string filePath)
     {
     {
@@ -34,30 +35,25 @@ public class MagicWandVisualizer
     {
     {
         Directory.EnumerateFiles(FilePath).ToList().ForEach(File.Delete);
         Directory.EnumerateFiles(FilePath).ToList().ForEach(File.Delete);
 
 
-        DrawingSurface surface = DrawingSurface.Create(new ImageInfo(width, height));
-        surface.Canvas.Clear(Colors.White);
-        Image previousImage = surface.Snapshot();
-        VecI scaledStart = new VecI(Steps[0].Start.X * (width / originalWidth), Steps[0].Start.Y * (height / originalHeight));
-        VecI scaledEnd = new VecI(Steps[0].End.X * (width / originalWidth), Steps[0].End.Y * (height / originalHeight));
-        
-        DrawArrow(surface, scaledStart, scaledEnd, 2, Colors.Green);
-        using (FileStream stream = new FileStream(Path.Join(FilePath, "Frame 1.png"), FileMode.Create))
-        {
-            surface.Snapshot().Encode().SaveTo(stream);
-        }
+        Image? previousImage = null;
 
 
-        for (int i = 1; i < Steps.Count; i++)
+        for (int i = 0; i < Steps.Count; i++)
         {
         {
-            surface = DrawingSurface.Create(new ImageInfo(width, height));
+            Step step = Steps[i];
+            var surface = DrawingSurface.Create(new ImageInfo(width, height));
             surface.Canvas.Clear(Colors.White);
             surface.Canvas.Clear(Colors.White);
-            surface.Canvas.DrawImage(previousImage, RectD.Create(VecI.Zero, new VecI(previousImage.Width, previousImage.Height)), replacementPaint);
-            
-            scaledStart = new VecI(Steps[i].Start.X * (width / originalWidth), Steps[i].Start.Y * (height / originalHeight));
-            scaledEnd = new VecI(Steps[i].End.X * (width / originalWidth), Steps[i].End.Y * (height / originalHeight));
+            if (previousImage != null)
+            {
+                surface.Canvas.DrawImage(previousImage,
+                    RectD.Create(VecI.Zero, new VecI(previousImage.Width, previousImage.Height)), replacementPaint);
+            }
+
+            var scaledStart = new VecI(step.Start.X * (width / originalWidth), step.Start.Y * (height / originalHeight));
+            var scaledEnd = new VecI(step.End.X * (width / originalWidth), step.End.Y * (height / originalHeight));
             
             
-            DrawArrow(surface, scaledStart, scaledEnd, 2, Colors.Green);
+            DrawArrow(surface, scaledStart, scaledEnd, 2, step.Type == StepType.Add ? Colors.Green : Colors.Red);
             
             
-            using (FileStream stream = new FileStream(Path.Join(FilePath, $"Frame {i}.png"), FileMode.Create))
+            using (FileStream stream = new FileStream(Path.Join(FilePath, $"Frame {i}_{CurrentContext}.png"), FileMode.Create))
             {
             {
                 surface.Snapshot().Encode().SaveTo(stream);
                 surface.Snapshot().Encode().SaveTo(stream);
             }
             }
@@ -86,3 +82,23 @@ public class MagicWandVisualizer
         surface.Canvas.DrawLine(end, arrowHead2, drawingPaint);
         surface.Canvas.DrawLine(end, arrowHead2, drawingPaint);
     }
     }
 }
 }
+
+public class Step
+{
+    public VecI Start { get; set; }
+    public VecI End { get; set; }
+    public StepType Type { get; set; }
+    
+    public Step(FloodFillHelper.Line line, StepType type = StepType.Add)
+    {
+        Start = line.Start;
+        End = line.End;
+        Type = type;
+    }
+}
+
+public enum StepType
+{
+    Add,
+    CancelLine
+}