みなさん、FloodFill を実行する方法はいくつかあります。それらはすべて問題を引き起こします。3 つの方法をリストし、それぞれで何が起こるかを説明します。誰かが私にいくつかの指針を与えることができれば、それは素晴らしいことです. 同様の投稿をいくつか見たことがありますが、C#、Java、または VB.net (私が知っている唯一の言語) に関するものはありません。
これは、CellColor メンバー変数に Color を格納する PixelData というクラスがあることを前提としています。「ピクセル」と呼ばれるサイズの PixelData オブジェクトの 50x50 の配列があります。この場合は 50 の CANVAS_SIZE という定数もあります。私が試した3つの方法を紹介します。
これは再帰的です。スタックオーバーフローが非常に発生しやすいです。このメソッドの完了後に CanFill メンバーを有効にするタイマーを設定しようとしました。これでもオーバーフローは防げません:
private void FloodFill(Point node, Color targetColor, Color replaceColor)
{
//perform bounds checking X
if ((node.X >= CANVAS_SIZE) || (node.X < 0))
return; //outside of bounds
//perform bounds checking Y
if ((node.Y >= CANVAS_SIZE) || (node.Y < 0))
return; //ouside of bounds
//check to see if the node is the target color
if (pixels[node.X, node.Y].CellColor != targetColor)
return; //return and do nothing
else
{
pixels[node.X, node.Y].CellColor = replaceColor;
//recurse
//try to fill one step to the right
FloodFill(new Point(node.X + 1, node.Y), targetColor, replaceColor);
//try to fill one step to the left
FloodFill(new Point(node.X - 1, node.Y), targetColor, replaceColor);
//try to fill one step to the north
FloodFill(new Point(node.X, node.Y - 1), targetColor, replaceColor);
//try to fill one step to the south
FloodFill(new Point(node.X, node.Y + 1), targetColor, replaceColor);
//exit method
return;
}
}
次に、キュー ベースの塗りつぶしを使用するメソッドがあります。このメソッドは、実行時に OutOfMemory Exceptions を引き起こし、キャンバス全体を塗りつぶすと非常に遅くなります。キャンバスのごく一部を埋めるだけであれば、ある程度効果的です。
private void QueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
Queue<Point> points = new Queue<Point>();
if (pixels[node.X, node.Y].CellColor != targetColor)
return;
points.Enqueue(node);
while (points.Count > 0)
{
Point n = points.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
pixels[n.X, n.Y].CellColor = replaceColor;
if (n.X != 0)
{
if (pixels[n.X - 1, n.Y].CellColor == targetColor)
points.Enqueue(new Point(n.X - 1, n.Y));
}
if (n.X != CANVAS_SIZE - 1)
{
if (pixels[n.X + 1, n.Y].CellColor == targetColor)
points.Enqueue(new Point(n.X + 1, n.Y));
}
if (n.Y != 0)
{
if (pixels[n.X, n.Y - 1].CellColor == targetColor)
points.Enqueue(new Point(n.X, n.Y - 1));
}
if (n.Y != CANVAS_SIZE - 1)
{
if (pixels[n.X, n.Y + 1].CellColor == targetColor)
points.Enqueue(new Point(n.X, n.Y + 1));
}
}
DrawCanvas();
return;
}
私が試した最後の方法でも、キュー ベースのフラッドフィルを使用します。この方法は、以前のキュー ベースのフラッドフィルよりもはるかに高速ですが、最終的には実行時に OutOfMemory 例外が発生します。繰り返しますが、ユーザーがすばやくクリックするのを防ぐ FillDelay タイマーを設定しようとしましたが、それでも例外の発生は止まりません。これに関するもう 1 つのバグは、小さな領域を適切に塗りつぶすのに苦労することです。クラッシュしないようにするまで、これを修正しても意味がありません。
private void RevisedQueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
Queue<Point> q = new Queue<Point>();
if (pixels[node.X, node.Y].CellColor != targetColor)
return;
q.Enqueue(node);
while (q.Count > 0)
{
Point n = q.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
{
Point e = n;
Point w = n;
while ((w.X != 0) && (pixels[w.X, w.Y].CellColor == targetColor))
{
pixels[w.X, w.Y].CellColor = replaceColor;
w = new Point(w.X - 1, w.Y);
}
while ((e.X != CANVAS_SIZE - 1) && (pixels[e.X, e.Y].CellColor == targetColor))
{
pixels[e.X, e.Y].CellColor = replaceColor;
e = new Point(e.X + 1, e.Y);
}
for (int i = w.X; i <= e.X; i++)
{
Point x = new Point(i, e.Y);
if (e.Y + 1 != CANVAS_SIZE - 1)
{
if (pixels[x.X, x.Y + 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y + 1));
}
if (e.Y - 1 != -1)
{
if (pixels[x.X, x.Y - 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y - 1));
}
}
}
}
}
みんなの助けに感謝します!これらのメソッドはすべて、wikipedia の疑似コードに基づいています。
編集:
RevisedQueueFloodFill を選択し、提案どおりに変更して、ループ内で変数が宣言されないようにしました。OutOfMemory は引き続き生成されます。filldelay タイマーを使用しても。
private void RevisedQueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
Queue<Point> q = new Queue<Point>();
if (pixels[node.X, node.Y].CellColor != targetColor)
return;
q.Enqueue(node);
Point n, e, w, x;
while (q.Count > 0)
{
n = q.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
{
e = n;
w = n;
while ((w.X != 0) && (pixels[w.X, w.Y].CellColor == targetColor))
{
pixels[w.X, w.Y].CellColor = replaceColor;
w = new Point(w.X - 1, w.Y);
}
while ((e.X != CANVAS_SIZE - 1) && (pixels[e.X, e.Y].CellColor == targetColor))
{
pixels[e.X, e.Y].CellColor = replaceColor;
e = new Point(e.X + 1, e.Y);
}
for (int i = w.X; i <= e.X; i++)
{
x = new Point(i, e.Y);
if (e.Y + 1 != CANVAS_SIZE - 1)
{
if (pixels[x.X, x.Y + 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y + 1));
}
if (e.Y - 1 != -1)
{
if (pixels[x.X, x.Y - 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y - 1));
}
}
}
}
}