如何验证2d位图是否连续?

时间:2010-09-10 19:57:24

标签: c# bitmap boolean area

假设你在C#中有以下结构:

struct Piece : IEquatable<Piece> {
    public readonly int size;
    public readonly bool[,] data;

    public Piece(Piece p) {
        size = p.size;

        data = (bool[,])p.data.Clone();
    }
    public Piece(int s, bool[,] d) {
        size = s;
        if (d.GetLength(0) != s || d.GetLength(1) != s) throw new ArgumentException();

        data = (bool[,])d.Clone();
    }

    public bool Equals(Piece other) {
        if (size != other.size) return false;

        return (data.Equals(other.data));
    }
}

这个想法是它代表一个size x size位表示一个形状(一个位图,如果你愿意的话)。

现在,并非所有可能的位组合都有效。我有一些要求:

  1. 总计必须只有size位。 (这很容易,我已经实现了这个)
  2. 所有位必须是连续的。
  3. 所以,再次假设size==4,以下是好的:

    ..#.
    ..#.
    .##.
    ....
    

    以下情况不是:

    ...#
    ...#
    #...
    #...
    

    如何判断所有位是否连续?

    编辑:这是完整的代码,包含Eric Lippert的答案。这绝对可以在性能方面得到收紧,但它非常易读。

    struct Point : IEquatable<Point> {
        public int X, Y;
    
        public Point(int x, int y) {
            X = x; Y = y;
        }
    
        public bool Equals(Point obj) {
            return X == obj.X && Y == obj.Y;
        }
    
        public override bool Equals(object obj) {
            if (obj == null) return false;
    
            if(obj is Point)
                return Equals((Point)obj);
    
            return false;
        }
    
        public override int GetHashCode() {
            return X ^ ~Y;
        }
    
        public static bool operator == (Point p1, Point p2) {
            return p1.X == p2.X && p1.Y == p2.Y;
        }
    
        public static bool operator !=(Point p1, Point p2) {
            return p1.X != p2.X || p1.Y != p2.Y;
        }
    
        public static readonly Point Empty = new Point(int.MinValue, int.MinValue);
    }
    
    struct Piece : IEquatable<Piece> {
        public readonly int size;
        public readonly bool[,] data;
        private bool valid;
    
        public Piece(Piece p) {
            size = p.size;
            valid = p.valid;
            data = (bool[,])p.data.Clone();
        }
        public Piece(int s, bool[,] d) {
            size = s;
            if (d.GetLength(0) != s || d.GetLength(1) != s) throw new ArgumentException();
    
            data = (bool[,])d.Clone();
            valid = false;
    
            CalcValidity();
        }
    
        public bool IsValid {
            get {
                return valid;
            }
        }
    
    
        private enum Square {
            White,
            Black,
            Red,
            Blue,
        }
    
        private int NumSquares(Square[,] map, Square q) {
            int ret = 0;
            for (int y = 0; y < size; y++) {
                for(int x = 0; x < size; x++) {
                    if (map[x, y] == q) ret++;
                }
            }
            return ret;
        }
    
        private Point PickSquare(Square[,] map, Square q) {
            for (int y = 0; y < size; y++) {
                for (int x = 0; x < size; x++) {
                    if (map[x, y] == q) return new Point(x, y);
                }
            }
            return Point.Empty;
        }
    
        private void MakeNeighboursRed(Square[,] map, Point p) {
            if (p.X > 0 && map[p.X - 1, p.Y] == Square.Black) map[p.X - 1, p.Y] = Square.Red;
            if (p.X < size - 1 && map[p.X + 1, p.Y] == Square.Black) map[p.X + 1, p.Y] = Square.Red;
    
            if (p.Y > 0 && map[p.X, p.Y - 1] == Square.Black) map[p.X, p.Y - 1] = Square.Red;
            if (p.Y < size - 1 && map[p.X, p.Y + 1] == Square.Black) map[p.X, p.Y + 1] = Square.Red;
        }
    
        private void CalcValidity() {
            Square[,] map = new Square[size, size];
    
            int count = 0;
            Point square = Point.Empty;
    
    
            for (int y = 0; y < size; y++) {
                for (int x = 0; x < size; x++) {
                    if (data[x, y]) {
                        map[x, y] = Square.Black;
                        square = new Point(x, y);
                    } else {
                        map[x, y] = Square.White;
                    }
                }
            }
    
            if (square != Point.Empty) {
                map[square.X, square.Y] = Square.Red;
            }
    
            while (true) {
                if (NumSquares(map, Square.Red) == 0) {
                    if (NumSquares(map, Square.Black) == 0) {
                        valid = count == size;
                        return;
                    } else {
                        valid = false;
                        return;
                    }
                } else {
                    square = PickSquare(map, Square.Red);
    
                    MakeNeighboursRed(map, square);
                    map[square.X, square.Y] = Square.Blue;
                    count++;
                }
            } 
        }
    
        #region IEquatable<Piece> Members
    
        public bool Equals(Piece other) {
            if (size != other.size) return false;
    
            for (int y = 0; y < size; y++) {
                for (int x = 0; x < size; x++) {
                    if (data[x, y] != other.data[x, y]) return false;
                }
            }
            return true;
        }
    
        #endregion
    }
    

2 个答案:

答案 0 :(得分:6)

答案 1 :(得分:0)

你从一个随机的“真实”位开始。然后你一步一步地“向北”,向南,向东和向西“走”。如果您发现“未访问”的“真实”位,请在单独的结构中将该节点标记为“已访问”,并从那里以所有方向递归“遍历”。如果该位为“假”或“已访问”,则不执行任何操作并返回到先前的“级别”。如果找不到更多非“访问”节点,请计算访问节点的数量,并与“真实”节点的总数进行比较。

编辑:请注意,如果位图很大,递归将耗尽堆栈空间。