在一系列连接点之间找到闭环

时间:2016-10-06 01:47:03

标签: c# unity3d

所以我一直在努力研究如何处理我最初的一个简单问题 - 结果我是个白痴,不知道我在做什么。

首先,我的数据结构如下:

public class Points{
    public List<Points> connectsTo = new List<Points>();
    public Vector3 position;
}
// main script 
List<Points> allWorldPoints = new List<Points>();

这个想法是点是连接器,创建墙壁,我需要找到房间。这是我想要实现的目标的图像:

enter image description here

房间不一定是方形/矩形,它们可以是L形或T形等。

问题是我不知道如何处理这个问题的逻辑,所以我正在寻找建议,因为逻辑真的让我感到困惑。

1 个答案:

答案 0 :(得分:3)

  1. 随时开始。
  2. 遍历连接点,直到您返回起点。从所有可能的路径中选择找到具有最少点数的路径;你刚刚找到一个房间。
  3. 存放新发现的房间。
  4. 选择一个不属于任何找到的房间的新起点并重复2。
  5. 没有分配给房间的点或没有找到更多封闭路径时结束。
  6. 顺便说一句,您的班级应该被命名为Point而不是Points

    更新:我添加了一个有效的例子。

    好的,首先让我们获得必要的基础设施。我将实现几个类:PointRoomImmutableStack<T>(后者用于简化遍历路径):

    public class ImmutableStack<T> : IEnumerable<T>
    {
        private readonly T head;
        private readonly ImmutableStack<T> tail;
        public int Count { get; }
        public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
    
        private ImmutableStack()
        {
            head = default(T);
            tail = null;
            Count = 0;
        }
    
        private ImmutableStack(T head, ImmutableStack<T> tail)
        {
            Debug.Assert(tail != null);
            this.head = head;
            this.tail = tail;
            Count = tail.Count + 1;
        }
    
        public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);
        public T Peek()
        {
            if (this == Empty)
                throw new InvalidOperationException("Can not peek an empty stack.");
    
            return head;
        }
    
        public ImmutableStack<T> Pop()
        {
            if (this == Empty)
                throw new InvalidOperationException("Can not pop an empty stack.");
    
            return tail;
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            var current = this;
    
            while (current != Empty)
            {
                yield return current.Peek();
                current = current.tail;
            }
        }
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        public override string ToString() => string.Join(" -> ", this);
    }
    
    public class Point: IEquatable<Point>
    {
        private readonly List<Point> connectedPoints;
        public int X { get; }
        public int Y { get; }
        public IEnumerable<Point> ConnectedPoints => connectedPoints.Select(p => p);
    
        public Point(int x, int y)
        {
            X = x;
            Y = y;
            connectedPoints = new List<Point>();
        }
    
        public void ConnectWith(Point p)
        {
            Debug.Assert(p != null);
            Debug.Assert(!Equals(p));
    
            if (!connectedPoints.Contains(p))
            {
                connectedPoints.Add(p);
                p.connectedPoints.Add(this);
            }
        }
    
        public bool Equals(Point p)
        {
            if (ReferenceEquals(p, null))
                return false;
    
            return X == p.X && Y == p.Y;
        }
    
        public override bool Equals(object obj) => this.Equals(obj as Point);
        public override int GetHashCode() => X ^ Y;
        public override string ToString() => $"[{X}, {Y}]";
    }
    
    public class Room
    {
        public IEnumerable<Point> Points { get; }
        public Room(IEnumerable<Point> points)
        {
            Points = points;
        }
    }
    

    好的,现在我们只执行上面列举的步骤:

    public static IEnumerable<Room> GetRooms(this IEnumerable<Point> points)
    {
        if (points.Count() < 3) //need at least 3 points to build a room
            yield break;
    
        var startCandidates = points;
    
        while (startCandidates.Any())
        {
            var start = startCandidates.First();
            var potentialRooms = GetPaths(start, start, ImmutableStack<Point>.Empty).OrderBy(p => p.Count);
    
            if (potentialRooms.Any())
            {
                var roomPath = potentialRooms.First();
                yield return new Room(roomPath);
                startCandidates = startCandidates.Except(roomPath);
            }
            else
            {
                startCandidates = startCandidates.Except(new[] { start });
            }
        }
    }
    
    private static IEnumerable<ImmutableStack<Point>> GetPaths(Point start, Point current, ImmutableStack<Point> path)
    {
        if (current == start &&
            path.Count > 2) //discard backtracking
        {
            yield return path;
        }
        else if (path.Contains(current))
        {
            yield break;
        }
        else
        {
            var newPath = path.Push(current);
    
            foreach (var point in current.ConnectedPoints)
            {
                foreach (var p in GetPaths(start, point, newPath))
                {
                    yield return p;
                }
            }
        }
    }
    

    当然,如果我们测试几何体:

        public static void Main(string[] args)
        {
            var p1 = new Point(0, 0);
            var p2 = new Point(0, 1);
            var p3 = new Point(0, 2);
            var p4 = new Point(1, 2);
            var p5 = new Point(1, 1);
            var p6 = new Point(1, 0);
            var p7 = new Point(2, 0);
            var p8 = new Point(2, 1);
            p1.ConnectWith(p2);
            p2.ConnectWith(p3);
            p3.ConnectWith(p4);
            p4.ConnectWith(p5);
            p5.ConnectWith(p6);
            p6.ConnectWith(p1);
            p6.ConnectWith(p7);
            p7.ConnectWith(p8);
            p8.ConnectWith(p5);
            var rooms = new[] { p1, p2, p3, p4, p5, p6, p7, p8 }.GetRooms();
        }
    

    我们得到了预期的两个房间。

    请注意,例如,可以将算法更高效地将ImmtuableStack更改为ImmutableHashSet

相关问题