C# 查找一系列连接点之间的闭合环
所以我一直在尝试如何解决我最初只是一个简单的问题——结果我是个白痴,根本不知道我在做什么 首先,我的数据结构如下:C# 查找一系列连接点之间的闭合环,c#,unity3d,C#,Unity3d,所以我一直在尝试如何解决我最初只是一个简单的问题——结果我是个白痴,根本不知道我在做什么 首先,我的数据结构如下: public class Points{ public List<Points> connectsTo = new List<Points>(); public Vector3 position; } // main script List<Points> allWorldPoints = new List<Points&
public class Points{
public List<Points> connectsTo = new List<Points>();
public Vector3 position;
}
// main script
List<Points> allWorldPoints = new List<Points>();
公共类积分{
public List connectsTo=新列表();
公共向量3位置;
}
//主脚本
List allWorldPoints=新列表();
我的想法是,这些点是创建墙的连接件,我需要从中找到房间。以下是我试图实现的目标:
房间不一定是方形/矩形,它们可以是L形或T形等
问题是我不知道如何解决这个问题的逻辑,所以我在寻求建议,因为这个逻辑真的让我困惑
Point
而不是Points
更新:我添加了一个工作示例
好的,首先让我们获得必要的基础设施。我将实现两个类:Point
、Room
和ImmutableStack
(后者用于简化遍历路径):
我们得到了预期的两个房间
请注意,例如,如果将
immutualStack
更改为ImmutableHashSet
,则可以提高算法的性能。仅列出点是不够的,您至少需要为每个连接和构建墙列出元组。我不明白,用洪水填房间可以吗?该视图显示了使用一个点(该点还包含其连接到的点的列表)进行连接的点。。还有为什么我需要我看不到那里的关系?编辑:unity似乎也不支持元组。我认为您使用的术语在代码中有点混乱。点
有一个点列表
。然后,您还创建了一个点列表
。是的,在我的主脚本中,这是一个点列表,每个点都有自己连接的点列表。什么是更好的结构?啊,那么一个点列表只适用于一个多边形?这有点小问题,如果你在房间1中看到,有一个点在左侧看起来基本上是多余的,这与点计数有点偏差。虽然这一点还有其他用途——因此点数可能不准确=/@Dave Flood fill,但我不知道这意味着什么@Martheen@Dave我看不出问题所在。该点将与其相邻点相连,因此将成为Room1路径的一部分。一旦它成为Room1的一部分,它的迪卡德就成为下一次搜索的出发点。@Dave谷歌它?这只是一个确定面积的算法。在你的例子中,你会得到3个区域,其中两个是房间。
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();
}