private void Run() { if (pList.Count > 0) { /* 初始化 */ v.pList.Clear(); v.vList.Clear(); record.Clear(); stepIndex = 0; ClearPaint(); /* 初始畫布建置 */ ClearPaint(); foreach (PointF i in pList) { DrawPointF(i, Color.DarkRed); } /* 排序點 */ pList = MathEx.SortPointF(pList); /* 畫 Convex Hull */ DrawConvexHull(MathEx.GetConvexHull(pList)); /* 畫 Voronoi Diagram */ v = VoronoiMultiPoint(pList); DrawVoronoiDiagram(v.vList, Color.DarkGreen); } }
/// <summary> /// 取得上下切線 /// </summary> /// <param name="pListL">左半部的點集合</param> /// <param name="pListR">左半部的點集合</param> /// <returns>上下切線</returns> public List <Edge> GetTangent(List <PointF> pListL, List <PointF> pListR) { List <Edge> tangent = new List <Edge>(); List <PointF> pList = new List <PointF>(); List <PointF> chList; pList.AddRange(pListL); pList.AddRange(pListR); pList = MathEx.SortPointF(pList); chList = MathEx.GetConvexHull(pList); for (int i = 0; i < chList.Count; i++) { if ((pListL.Contains(chList[i]) && pListR.Contains(chList[(i + 1) % chList.Count])) || (pListR.Contains(chList[i]) && pListL.Contains(chList[(i + 1) % chList.Count]))) { tangent.Add(new Edge(chList[i], chList[(i + 1) % chList.Count])); } } record.Add(new Record(chList, tangent, CONVEX_HULL, true)); return(tangent); }
/// <summary> /// 合併左右半部的 Voronoi Diagram /// </summary> /// <param name="vL">左半部的 Voronoi Diagram</param> /// <param name="vR">右半部的 Voronoi Diagram</param> /// <returns>合併的 Voronoi Diagram</returns> public Voronoi Merge(Voronoi vL, Voronoi vR) { Voronoi v = new Voronoi(); try { /* 找上切線和下切線 */ List <PointF> chL = MathEx.GetConvexHull(vL.pList); record.Add(new Record(chL, CONVEX_HULL)); List <PointF> chR = MathEx.GetConvexHull(vR.pList); record.Add(new Record(chR, CONVEX_HULL)); List <Edge> tangent = GetTangent(chL, chR); #region 找 Hyper Plane List <Edge> hpList = new List <Edge>(); // Hyper Plane PointF? p; // 暫存 Hyper Plane 與 Voronoi 的交點 PointF? nearPoint = null; // Hyper Plane 與 Voronoi 最先碰到的交點 PointF? lastNearPoint = null; // 上一個 nearPoint Edge scan; // 掃描線段 Edge candidate = new Edge(); // 碰到的線段 Edge last = new Edge(); // 上一個碰到的線段 Edge hyperPlane = new Edge(); // 當前的 Hyper Plane List <int> eliminate = new List <int>(); // 需要消線的索引 List <int> delete = new List <int>(); // 需要刪線的索引 foreach (PointF i in vL.pList) { v.pList.Add(new PointF(i.X, i.Y)); } foreach (PointF i in vR.pList) { v.pList.Add(new PointF(i.X, i.Y)); } foreach (Edge i in vL.vList) { v.vList.Add(new Edge(i.A, i.B, i.a, i.b)); } foreach (Edge i in vR.vList) { v.vList.Add(new Edge(i.A, i.B, i.a, i.b)); } /* 掃描線從上切線進入 */ scan = new Edge(tangent[0].A, tangent[0].B); lastNearPoint = scan.GetBisector().A; while (!scan.Equals(tangent[1])) { hyperPlane = scan.GetBisector(); /* 找最先碰到的線的交點 */ nearPoint = null; for (int i = 0; i < v.vList.Count; i++) { if (last != null && last.Equals(v.vList[i])) { continue; } /* 找交點且 Hyper Plane 不能回頭 */ if ((p = MathEx.GetIntersection(hyperPlane, v.vList[i])) != null && Math.Round(Convert.ToDouble(((PointF)lastNearPoint).Y)) >= ((PointF)p).Y) { /* 找到第一個交點 */ if (nearPoint == null) { nearPoint = p; candidate = v.vList[i]; continue; } if (MathEx.GetDistance(hyperPlane.A, (PointF)p) < MathEx.GetDistance(hyperPlane.A, (PointF)nearPoint)) { nearPoint = p; candidate = v.vList[i]; } } } /* 從上一段 Hyper Plane 接著畫 */ if (lastNearPoint != null) { hyperPlane.A = (PointF)lastNearPoint; } hpList.Add(new Edge(hyperPlane.A, (PointF)nearPoint, scan.A, scan.B)); eliminate.Add(v.vList.IndexOf(candidate)); last = candidate; lastNearPoint = nearPoint; /* 尋找下一條掃描線 */ if (scan.A.Equals(candidate.a)) { scan.A = candidate.b; } else if (scan.A.Equals(candidate.b)) { scan.A = candidate.a; } else if (scan.B.Equals(candidate.a)) { scan.B = candidate.b; } else if (scan.B.Equals(candidate.b)) { scan.B = candidate.a; } } /* 上切線等於下切線:共線 */ if (tangent[0].Equals(tangent[1])) { hpList.Add(new Edge(tangent[0].GetBisector().A, tangent[0].GetBisector().B, scan.A, scan.B)); } else { hpList.Add(new Edge((PointF)nearPoint, tangent[1].GetBisector().A, scan.A, scan.B)); } record.Add(new Record(hpList, HYPER_LINE)); #endregion #region 消線 for (int i = 0; i < eliminate.Count; i++) { if (MathEx.Cross(hpList[i].A, hpList[i].B, hpList[i + 1].B) >= 0) { if (MathEx.Cross(hpList[i].A, hpList[i].B, v.vList[eliminate[i]].A) > 0) { /* 消除延伸線 */ foreach (Edge j in v.vList) { if (j.A.Equals(v.vList[eliminate[i]].A) && !j.B.Equals(v.vList[eliminate[i]].B)) { if (MathEx.Cross(hpList[i].B, j.A, j.B) > 0) { delete.Add(v.vList.IndexOf(j)); } } else if (j.B.Equals(v.vList[eliminate[i]].A) && !j.A.Equals(v.vList[eliminate[i]].B)) { if (MathEx.Cross(hpList[i].B, j.B, j.A) > 0) { delete.Add(v.vList.IndexOf(j)); } } } v.vList[eliminate[i]].A = hpList[i].B; } else { /* 消除延伸線 */ foreach (Edge j in v.vList) { if (j.A.Equals(v.vList[eliminate[i]].B) && !j.B.Equals(v.vList[eliminate[i]].A)) { if (MathEx.Cross(hpList[i].B, j.A, j.B) > 0) { delete.Add(v.vList.IndexOf(j)); } } else if (j.B.Equals(v.vList[eliminate[i]].B) && !j.A.Equals(v.vList[eliminate[i]].A)) { if (MathEx.Cross(hpList[i].B, j.B, j.A) > 0) { delete.Add(v.vList.IndexOf(j)); } } } v.vList[eliminate[i]].B = hpList[i].B; } } else if (MathEx.Cross(hpList[i].A, hpList[i].B, hpList[i + 1].B) < 0) { if (MathEx.Cross(hpList[i].A, hpList[i].B, v.vList[eliminate[i]].A) < 0) { /* 消除延伸線 */ foreach (Edge j in v.vList) { if (j.A.Equals(v.vList[eliminate[i]].A) && !j.B.Equals(v.vList[eliminate[i]].B)) { if (MathEx.Cross(hpList[i].B, j.A, j.B) < 0) { delete.Add(v.vList.IndexOf(j)); } } else if (j.B.Equals(v.vList[eliminate[i]].A) && !j.A.Equals(v.vList[eliminate[i]].B)) { if (MathEx.Cross(hpList[i].B, j.B, j.A) < 0) { delete.Add(v.vList.IndexOf(j)); } } } v.vList[eliminate[i]].A = hpList[i].B; } else { /* 消除延伸線 */ foreach (Edge j in v.vList) { if (j.A.Equals(v.vList[eliminate[i]].B) && !j.B.Equals(v.vList[eliminate[i]].A)) { if (MathEx.Cross(hpList[i].B, j.A, j.B) < 0) { delete.Add(v.vList.IndexOf(j)); } } else if (j.B.Equals(v.vList[eliminate[i]].B) && !j.A.Equals(v.vList[eliminate[i]].A)) { if (MathEx.Cross(hpList[i].B, j.B, j.A) < 0) { delete.Add(v.vList.IndexOf(j)); } } } v.vList[eliminate[i]].B = hpList[i].B; } } } /* 刪除延伸線 */ delete = delete.OrderByDescending(X => X).Distinct().ToList(); foreach (int i in delete) { v.vList.RemoveAt(i); } record.Add(new Record(v.vList, VORONOI, true)); #endregion foreach (Edge i in hpList) { v.vList.Add(new Edge(i.A, i.B, i.a, i.b)); } record.Add(new Record(v.vList, MERGE, true)); } catch (Exception e) { //MessageBox.Show("Coming Soon...", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); } return(v); }