微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

找出一系列连接点之间的闭环

因此,我一直在尝试解决最初是一个简单问题的方法-原来我是个白痴,不知道自己在做什么.

首先,我的数据结构是这样的:

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形等.

问题是我不知道如何解决此问题的逻辑,因此我正在寻找建议,因为该逻辑确实使我感到困惑.

解决方法:

>随时开始.
>遍历连接的点,直到回到起点.从所有可能的路径中选择找到的点数最少的路径;您刚刚找到了一个房间.
>存放新发现的房间.
>选择不属于任何已找到房间的新起点,然后重复2.
>当没有剩余点未分配给房间或找不到更多封闭路径时结束.

顺便说一句,您的班级应命名为Point而不是Points.

更新:我添加一个有效的示例.

好的,首先让我们获得必要的基础架构.我将实现几个类:Point,Room和ImmutableStack< 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可使算法更有效.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐