Пример #1
0
		/// <summary>
		/// 创建从src到dest的边, 如果它已经存在, 直接返回它.
		/// </summary>
		public HalfEdge CreateEdge(Vertex src, Vertex dest)
		{
			HalfEdge self = GetRays(src).Find(item => { return item.Dest == dest; });

			// 如果该边不存在, 进行创建.
			if (self == null)
			{
				self = new HalfEdge();
				self.Dest = dest;

				HalfEdge other = new HalfEdge();
				other.Dest = src;

				self.Pair = other;
				other.Pair = self;

				/*src.Edge = self;
				dest.Edge = other;
				*/

				AddUnserializedEdge(self);
				AddUnserializedEdge(other);
			}

			return self;
		}
Пример #2
0
		/// <summary>
		/// 获取可以经过a, b边的物体的最大半径(a, b必须有交点).
		/// </summary>
		public float GetWidth(HalfEdge a, HalfEdge b)
		{
			Vertex v = GetIntersectVertex(a, b);
			if (v == A)
			{
				if (float.IsNaN(widthA)) { widthA = CalculateWidth(a, b); }
				return widthA;
			}

			if (v == B)
			{
				if (float.IsNaN(widthB)) { widthB = CalculateWidth(a, b); }
				return widthB;
			}

			if (v == C)
			{
				if (float.IsNaN(widthC)) { widthC = CalculateWidth(a, b); }
				return widthC;
			}

			Utility.Verify(false, "Invalid edges {0} and {1}", a, b);
			return float.NaN;
		}
Пример #3
0
		/// <summary>
		/// 查找ray的环中, 与src->dest的边相交的边.
		/// </summary>
		bool FindCrossedEdge(out Tuple2<HalfEdge, CrossState> answer, HalfEdge ray, Vector3 src, Vector3 dest)
		{
			List<HalfEdge> cycle = ray.Cycle;
			answer = new Tuple2<HalfEdge, CrossState>();

			foreach (HalfEdge edge in cycle)
			{
				Vector3 point;
				CrossState crossState = MathUtility.GetLineCrossPoint(out point,
					edge.Src.Position, edge.Dest.Position,
					src, dest
				);

				if (crossState == CrossState.FullyOverlaps
					|| (crossState == CrossState.CrossOnSegment && !point.equals2(edge.Src.Position) && !point.equals2(edge.Dest.Position)))
				{
					answer.Second = crossState;
					answer.First = edge;
					if (crossState == CrossState.FullyOverlaps
						&& (dest.equals2(edge.Src.Position) || src.equals2(edge.Dest.Position)))
					{
						answer.First = answer.First.Pair;
					}

					return true;
				}
			}

			return false;
		}
Пример #4
0
		/// <summary>
		/// 将点插入到old的边上.
		/// </summary>
		void InsertOnEdge(Vertex v, Triangle old, HalfEdge hitEdge)
		{
			// 连接v和v所在的边正对的顶点, 将old一分为二.
			Triangle split1 = geomManager.CreateTriangle();
			Triangle split2 = geomManager.CreateTriangle();

			Vertex opositeVertex = hitEdge.Next.Dest;

			HalfEdge ov = geomManager.CreateEdge(opositeVertex, v);
			HalfEdge v1 = geomManager.CreateEdge(v, hitEdge.Dest);
			HalfEdge v2 = geomManager.CreateEdge(v, hitEdge.Pair.Dest);

			HalfEdge sp1Edge0 = hitEdge.Next;

			HalfEdge sp2Edge0 = hitEdge.Next.Next;
			HalfEdge sp2Edge1 = v2.Pair;
			HalfEdge sp2Edge2 = ov.Pair;

			// 更新Face.
			sp1Edge0.Face = ov.Face = v1.Face = split1;
			sp2Edge0.Face = sp2Edge1.Face = sp2Edge2.Face = split2;

			// 释放旧三角形.
			geomManager.ReleaseTriangle(old);

			// 连接边.
			split1.Edge = sp1Edge0.CycleLink(ov, v1);
			split2.Edge = sp2Edge0.CycleLink(sp2Edge1, sp2Edge2);

			// 将新三角形映射到格子地图上.
			geomManager.RasterizeTriangle(split1);
			geomManager.RasterizeTriangle(split2);

			Utility.Verify(ov.Face == split1);
			Utility.Verify(ov.Pair.Face == split2);

			Triangle other = hitEdge.Pair.Face;

			Triangle oposite1 = null;
			Triangle oposite2 = null;

			// 分割另一侧的三角形.
			if (other != null)
			{
				Vertex p = hitEdge.Pair.Next.Dest;

				HalfEdge vp = geomManager.CreateEdge(v, p);

				oposite1 = geomManager.CreateTriangle();
				oposite2 = geomManager.CreateTriangle();

				HalfEdge hpn = hitEdge.Pair.Next;
				HalfEdge op1Edge0 = hpn.Next;
				HalfEdge op1Edge1 = v1.Pair;
				HalfEdge op1Edge2 = vp;

				hpn.Face = vp.Pair.Face = v2.Face = oposite2;
				op1Edge0.Face = op1Edge1.Face = op1Edge2.Face = oposite1;
				geomManager.ReleaseTriangle(other);

				oposite2.Edge = hpn.CycleLink(vp.Pair, v2);
				oposite1.Edge = op1Edge0.CycleLink(op1Edge1, op1Edge2);

				geomManager.RasterizeTriangle(oposite1);
				geomManager.RasterizeTriangle(oposite2);

				Utility.Verify(vp.Face == oposite1);
				Utility.Verify(vp.Pair.Face == oposite2);
			}

			Utility.Verify(v1.Face == split1);
			Utility.Verify(v1.Pair.Face == oposite1);

			Utility.Verify(v2.Face == oposite2);
			Utility.Verify(v2.Pair.Face == split2);

			// 维护delaunay特性.
			FlipTriangles(split1.Edge);
			FlipTriangles(split2.Edge);

			if (other != null)
			{
				FlipTriangles(oposite1.Edge);
				FlipTriangles(oposite2.Edge);
			}
		}
Пример #5
0
		/// <summary>
		/// 构造src到dest的约束边. crossedEdge为第一条相交边.
		/// </summary>
		HalfEdge OnConstrainedEdgeCrossEdges(ref Vertex src, Vertex dest, HalfEdge crossedEdge)
		{
			List<Vertex> upper = new List<Vertex>();
			List<Vertex> lower = new List<Vertex>();
			List<HalfEdge> edges = new List<HalfEdge>();

			// 收集相交的非约束边.
			Vertex newSrc = CollectCrossedUnconstrainedEdges(edges, lower, upper, src, dest, crossedEdge);

			// 清理非约束边两侧的三角形.
			for (int i = 0; i < edges.Count; ++i)
			{
				HalfEdge edge = edges[i];
				if (edge.Face != null)
				{
					geomManager.ReleaseTriangle(edge.Face);
				}

				if (edge.Pair.Face != null)
				{
					geomManager.ReleaseTriangle(edge.Pair.Face);
				}
			}

			// 对清理过后的区域三角形化(该区域不一定是多边形).
			CreateTriangles(PolygonTriangulation.Triangulate(lower, dest, src));
			CreateTriangles(PolygonTriangulation.Triangulate(upper, src, dest));

			// 标记约束边.
			HalfEdge constrainedEdge = geomManager.GetRays(src).Find(edge => { return edge.Dest == dest; });
			Utility.Verify(constrainedEdge != null);
			constrainedEdge.Constrained = true;

			src = newSrc;
			return constrainedEdge;
		}
Пример #6
0
		/// <summary>
		/// 收集与src到dest相交的约束边.
		/// </summary>
		Vertex CollectCrossedUnconstrainedEdges(List<HalfEdge> answer, List<Vertex> lowerVertices, List<Vertex> upperVertices, Vertex src, Vertex dest, HalfEdge start)
		{
			// src->dest向量.
			Vector3 srcDest = dest.Position - src.Position;

			Vertex current = src;

			// 遍历到dest所在的三角形为止.
			for (; !start.Face.Contains(dest.Position); )
			{
				Utility.Verify(!start.Constrained, "Crossed constrained edge");
				// 收集相交的边.
				answer.Add(start);

				HalfEdge opposedTriangle = start.Face.GetOpposite(current);

				Utility.Verify(opposedTriangle != null);

				Vertex opposedVertex = opposedTriangle.Next.Dest;
				if ((opposedVertex.Position - src.Position).cross2(srcDest) < 0)
				{
					current = opposedTriangle.Src;
				}
				else
				{
					current = opposedTriangle.Dest;
				}

				float cr = (opposedTriangle.Dest.Position - src.Position).cross2(srcDest);
				Utility.Verify(!MathUtility.Approximately(0, cr), "Not implement");

				List<Vertex> activeContainer = ((cr < 0) ? upperVertices : lowerVertices);

				if (!activeContainer.Contains(opposedTriangle.Dest)) { activeContainer.Add(opposedTriangle.Dest); }

				cr = (opposedTriangle.Src.Position - src.Position).cross2(srcDest);
				activeContainer = ((cr < 0) ? upperVertices : lowerVertices);

				if (!activeContainer.Contains(opposedTriangle.Src)) { activeContainer.Add(opposedTriangle.Src); }

				if (MathUtility.PointOnSegment(opposedVertex.Position, src.Position, dest.Position))
				{
					answer.Add(opposedTriangle);
					src = opposedVertex;
					break;
				}

				start = opposedTriangle;
			}

			return src;
		}
Пример #7
0
		/// <summary>
		/// 判断半径为radius的物体是否可以通过prevPortal和currentPortal.
		/// </summary>
		static bool CheckCorridorWidthLimit(HalfEdge prevPortal, HalfEdge currentPortal, float radius)
		{
			if (prevPortal == null)
			{
				return true;
			}

			return currentPortal.Pair.Face.GetWidth(prevPortal, currentPortal.Pair) >= (radius * 2f);
		}
Пример #8
0
		/// <summary>
		/// from在edge上, 查找半径为radius的物体从from到to可达的最远位置.
		/// </summary>
		Vector3 RaycastFromEdge(HalfEdge edge, Vector3 from, Vector3 to, float radius)
		{
			// 起点==终点, 或者edge为约束边.
			if (from.equals2(to) || edge.Constrained || edge.Pair.Constrained)
			{
				return from;
			}

			// from位置无效.
			if (!IsValidPosition(from, radius))
			{
				return from;
			}

			// 根据from->to的方向, 确定下一个要访问的三角形是edge.Face还是edge.Pair.Face.
			if ((to - from).cross2(edge.Dest.Position - edge.Src.Position) > 0)
			{
				edge = edge.Pair;
			}

			// 不存在另一边的三角形.
			if (edge.Face == null)
			{
				return from;
			}

			// 检查另外两条边.
			return RaycastWithEdges(new HalfEdge[] { edge.Next, edge.Next.Next }, from, to, radius);
		}
Пример #9
0
		/// <summary>
		/// 获取ea和eb的交点.
		/// </summary>
		Vertex GetIntersectVertex(HalfEdge ea, HalfEdge eb)
		{
			if (ea.Src == eb.Dest) { return ea.Src; }
			if (ea.Dest == eb.Src) { return ea.Dest; }
			Utility.Verify(false, "Edge {0} and {1} has no intersection", ea, eb);
			return null;
		}
Пример #10
0
		float SearchWidth(Vertex c, Triangle t, HalfEdge e, float d)
		{
			Vertex u = e.Src, v = e.Dest;
			if (c.Position.dot2(v.Position, u.Position) <= 0
				|| c.Position.dot2(u.Position, v.Position) <= 0)
			{
				return d;
			}

			float d2 = MathUtility.MinDistance2Segment(c.Position, e.Src.Position, e.Dest.Position);
			if (d2 > d)
			{
				return d;
			}

			if (e.Constrained)
			{
				return d2;
			}

			Triangle t2 = e.Pair.Face;

			if (t2 == null)
			{
				Debug.LogError("Invalid t2");
				return d;
			}

			HalfEdge e2 = t2.GetOpposite(e.Src);
			HalfEdge e3 = t2.GetOpposite(e.Dest);

			d = SearchWidth(c, t2, e2, d);
			return SearchWidth(c, t2, e3, d);
		}
Пример #11
0
		void ReleaseHalfEdge(HalfEdge edge)
		{
			List<HalfEdge> list = halfEdgeContainer[edge.Src];
			Utility.Verify(list.Remove(edge));
			if (list.Count == 0)
			{
				halfEdgeContainer.Remove(edge.Src);
			}
		}
Пример #12
0
		/// <summary>
		/// 反序列化边.
		/// </summary>
		public void ReadBinary(BinaryReader reader, List<Vertex> vertices, IDictionary<int, HalfEdge> container)
		{
			container[ID] = this;

			int destVertexID = reader.ReadInt32();

			Dest = vertices.Find(item => { return item.ID == destVertexID; });
			Utility.Verify(Dest != null);

			int nextEdge = reader.ReadInt32();

			HalfEdge edge = null;
			if (nextEdge != -1 && !container.TryGetValue(nextEdge, out edge))
			{
				container.Add(nextEdge, edge = new HalfEdge());
				edge.ID = nextEdge;
			}
			Next = edge;

			int pairEdge = reader.ReadInt32();

			if (!container.TryGetValue(pairEdge, out edge))
			{
				container.Add(pairEdge, edge = new HalfEdge());
				edge.ID = pairEdge;
			}
			Pair = edge;

			Utility.Verify(Pair != null);

			Constrained = reader.ReadBoolean();

			// Face字段由Triangle来更新.
		}
Пример #13
0
		void AddEdge(HalfEdge edge)
		{
			List<HalfEdge> list = null;
			Utility.Verify(edge.Pair != null, "Invalid Edge, ID = " + edge.ID);

			if (!halfEdgeContainer.TryGetValue(edge.Src, out list))
			{
				halfEdgeContainer.Add(edge.Src, list = new List<HalfEdge>());
			}

			list.Add(edge);
		}
Пример #14
0
		/// <summary>
		/// 向管理器加入一条反序列化后的边.
		/// <para>仅供反序列化时使用!</para>
		/// </summary>
		public void AddUnserializedEdge(HalfEdge edge)
		{
			AddEdge(edge);
		}
Пример #15
0
		/// <summary>
		/// 释放edge及edge.Pair.
		/// </summary>
		public void ReleaseEdge(HalfEdge edge)
		{
			ReleaseHalfEdge(edge.Pair);
			ReleaseHalfEdge(edge);
		}
Пример #16
0
		/// <summary>
		/// 重置寻路使用的数据.
		/// </summary>
		public void ClearPathfinding()
		{
			G = H = float.PositiveInfinity;
			Flag = -1;
			Portal = null;
		}
Пример #17
0
		/// <summary>
		/// 获取圆心在portal1和portal2的交点, 角度为二者的夹角, 半径为radius的圆弧的长度.
		/// </summary>
		public static float CalculateArcLengthBetweenPortals(HalfEdge portal1, HalfEdge portal2, float radius)
		{
			Vector3 v1 = Vector3.zero, v2 = Vector3.zero;

			if (portal1.Src == portal2.Src)
			{
				v1 = portal1.Dest.Position - portal1.Src.Position; 
				v2 = portal2.Dest.Position - portal1.Src.Position;
			}
			else if (portal1.Dest == portal2.Dest)
			{
				v1 = portal1.Src.Position - portal1.Dest.Position;
				v2 = portal2.Src.Position - portal1.Dest.Position;
			}
			else
			{
				Utility.Verify(false, "failed to find common vertex, portals are {0} and {1}", portal1, portal2);
			}

			return Mathf.Acos(v1.dot2(v2) / (v1.magnitude2() * v2.magnitude2())) * radius;
		}
Пример #18
0
		/// <summary>
		/// 获取边from"正对"的顶点.
		/// </summary>
		public Vertex GetOpposite(HalfEdge from)
		{
			if (AB.ID == from.ID) { return C; }
			if (BC.ID == from.ID) { return A; }
			if (CA.ID == from.ID) { return B; }
			Utility.Verify(false, "Invalid argument");
			return null;
		}
Пример #19
0
		/// <summary>
		/// 从reader中创建/查找边, 并初始化.
		/// </summary>
		public HalfEdge CreateEdge(BinaryReader reader, List<Vertex> vertices, IDictionary<int, HalfEdge> container)
		{
			HalfEdge answer = null;
			int edgeID = reader.ReadInt32();

			if (!container.TryGetValue(edgeID, out answer))
			{
				container.Add(edgeID, answer = new HalfEdge());
				answer.ID = edgeID;
			}

			answer.ReadBinary(reader, vertices, container);
			return answer;
		}
Пример #20
0
		/// <summary>
		/// 计算可以经过a, b边的物体的最大半径(a, b必须有交点).
		/// </summary>
		float CalculateWidth(HalfEdge ea, HalfEdge eb)
		{
			Vertex vc = GetIntersectVertex(ea, eb);

			Utility.Verify(vc != null);

			HalfEdge ec = GetOpposite(vc);
			Vertex va = GetOpposite(ea);
			Vertex vb = GetOpposite(eb);

			float d = (ea.Src.Position - ea.Dest.Position).magnitude2();
			d = Mathf.Min(d, (eb.Src.Position - eb.Dest.Position).magnitude2());

			if (vc.Position.dot2(vb.Position, va.Position) <= 0
				|| vc.Position.dot2(va.Position, vb.Position) <= 0)
			{
				return d;
			}

			if (ec.Constrained)
			{
				return MathUtility.MinDistance2Segment(vc.Position, ec.Src.Position, ec.Dest.Position);
			}

			return SearchWidth(vc, this, ec, d);
		}
Пример #21
0
		/// <summary>
		/// 检查halfEdge两侧的三角形是否满足delaunay性质. 如果不满足, 进行翻转.
		/// </summary>
		void FlipTriangles(HalfEdge halfEdge)
		{
			Stack<HalfEdge> stack = new Stack<HalfEdge>();
			stack.Push(halfEdge);

			for (; stack.Count != 0; )
			{
				halfEdge = stack.Pop();

				// 如果该边是约束边, 不翻转.
				if (halfEdge.Constrained || halfEdge.Pair.Constrained) { continue; }

				Triangle x = halfEdge.Face;
				Triangle y = halfEdge.Pair.Face;

				if (x == null || y == null) { continue; }

				Utility.Verify(x.Walkable && y.Walkable, "Can not flip unwalkable triangle");

				// 检查是否满足delaunay性质.
				if (!MathUtility.PointInCircumCircle(x.A.Position, x.B.Position, x.C.Position, halfEdge.Pair.Next.Dest.Position))
				{
					continue;
				}

				// 创建新的边.
				HalfEdge ab = geomManager.CreateEdge(halfEdge.Next.Dest, halfEdge.Pair.Next.Dest);

				HalfEdge bEdges0 = halfEdge.Pair.Next.Next;
				HalfEdge bEdges1 = halfEdge.Next;
				HalfEdge bEdges2 = ab;

				// 去掉映射.
				// 注意这里不删除三角形, 而是通过设置边, 将旧三角形改造为新的.
				geomManager.UnrasterizeTriangle(x);
				geomManager.UnrasterizeTriangle(y);

				// 连接新的边.
				x.Edge = halfEdge.Next.Next.CycleLink(halfEdge.Pair.Next, ab.Pair);
				y.Edge = bEdges0.CycleLink(bEdges1, bEdges2);

				// 映射新三角形.
				geomManager.RasterizeTriangle(x);
				geomManager.RasterizeTriangle(y);

				x.BoundingEdges.ForEach(item => { item.Face = x; });
				y.BoundingEdges.ForEach(item => { item.Face = y; });

				// 翻转之后, 检查这2个三角形的另外2条边, 是否满足delaunay性质.
				if (GuardedPushStack(stack, halfEdge.Pair.Next.Next)
					&& GuardedPushStack(stack, halfEdge.Next)
					&& GuardedPushStack(stack, halfEdge.Pair.Next)
					&& GuardedPushStack(stack, halfEdge.Next.Next))
				{
				}

				halfEdge.Face = halfEdge.Pair.Face = null;

				// 该边已不存在, 将他删除.
				geomManager.ReleaseEdge(halfEdge);
			}
		}
Пример #22
0
		/// <summary>
		/// 入栈.
		/// </summary>
		bool GuardedPushStack(Stack<HalfEdge> stack, HalfEdge item)
		{
			if (stack.Count < EditorConstants.kMaxStackCapacity)
			{
				stack.Push(item);
				return true;
			}

			return false;
		}
Пример #23
0
		/// <summary>
		/// 判断半径为radius的物体, 可否通过portal进入currentNode.
		/// </summary>
		static bool CheckEntryAndExitWidthLimit(PathfindingNode currentNode, PathfindingNode destNode, HalfEdge portal, float radius)
		{
			// 如果已经到达最终节点, 只判断可否经由此边进入.
			if (currentNode == destNode)
			{
				return (portal.Dest.Position - portal.Src.Position).magnitude2() >= radius * 2;
			}

			// 否则, 除了判断, 是否可以经由次边进入外, 仍需判断该节点是否存在其他的边, 供离开.
			HalfEdge other1 = portal.Face.AB, other2 = portal.Face.BC;
			if (portal == portal.Face.AB)
			{
				other1 = portal.Face.BC;
				other2 = portal.Face.CA;
			}
			else if (portal == portal.Face.BC)
			{
				other1 = portal.Face.AB;
				other2 = portal.Face.CA;
			}

			// 其他两条边是否可供离开.
			float diameter = radius * 2f;
			if (portal.Face.GetWidth(portal, other1) >= diameter
				|| portal.Face.GetWidth(portal, other2) >= diameter)
			{
				return true;
			}

			return false;
		}