public Record(Voronoi v, bool clear = false) { this.eList = v.vList; this.type = 1; this.clear = clear; this.enable = true; }
private void Fresh() { // 调整窗口大小 Size = new Size(xSize + ControlPanel.Size.Width + Size.Width - ClientSize.Width, ySize + Size.Height - ClientSize.Height); // 刷新画板 DrawingBroad.Image = new Bitmap(xSize, ySize); // 初始点集 foreach (var point in pointList) { DrawPoint((float)point.X, (float)point.Y, Color.Blue); } Voronoi v = new Voronoi(pointList, xSize, ySize, precision); // 画维诺图 var segments = v.QuerySegments(); foreach (var seg in segments) { DrawLine(seg, Color.Black); } // 画三角网 //foreach (var site in v.Sites) //{ // var face = v.QueryFace(site); // foreach (var seg in face.Segments) // { // var edge = new Segment(seg.F1.Site, seg.F2.Site); // DrawLine(edge, Color.Red); // } //} }
/// <summary> /// 計算多個點的Voronoi Diagram /// </summary> /// <returns>Voronoi Diagram</returns> public Voronoi VoronoiMultiPoint(List <PointF> pList) { Voronoi v = new Voronoi(); if (pList.Count == 1) { return(v); } else if (pList.Count == 2) { Voronoi twoVoronoi = VoronoiTwoPoint(pList); record.Add(new Record(twoVoronoi.vList, VORONOI)); return(twoVoronoi); } else if (pList.Count == 3) { Voronoi threeVoronoi = VoronoiThreePoint(pList); record.Add(new Record(threeVoronoi.vList, VORONOI)); return(threeVoronoi); } else if (pList.Count > 3) { List <PointF> pListL; List <PointF> pListR; MathEx.Divide(pList, out pListL, out pListR); v = Merge(VoronoiMultiPoint(pListL), VoronoiMultiPoint(pListR)); } return(v); }
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); } }
public Voronoi VoronoiThreePoint(PointF A, PointF B, PointF C) { Voronoi v = new Voronoi(); PointF triEx; PointF mid; v.pList.Add(A); v.pList.Add(B); v.pList.Add(C); /* 三點共線 */ if (MathEx.isCollinear(v.pList[0], v.pList[1], v.pList[2])) { /* 將點排序 */ v.pList = MathEx.SortPointF(v.pList); /* 將邊無限延伸的做法 */ mid = MathEx.GetMidPointF(v.pList[0], v.pList[1]); v.vList.Add(new Edge(mid.Add(MathEx.GetNormalVector(v.pList[0], v.pList[1]).Multi(600)), mid.Add(MathEx.GetNormalVector(v.pList[1], v.pList[0]).Multi(600)), v.pList[0], v.pList[1])); mid = MathEx.GetMidPointF(v.pList[1], v.pList[2]); v.vList.Add(new Edge(mid.Add(MathEx.GetNormalVector(v.pList[1], v.pList[2]).Multi(600)), mid.Add(MathEx.GetNormalVector(v.pList[2], v.pList[1]).Multi(600)), v.pList[1], v.pList[2])); /* 將邊畫到邊界的做法 * mid = MathEx.GetMidPointF(v.pList[0], v.pList[1]); * v.vList.Add(new Edge(GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(v.pList[1], v.pList[0]))), GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(v.pList[0], v.pList[1]))))); * mid = MathEx.GetMidPointF(v.pList[1], v.pList[2]); * v.vList.Add(new Edge(GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(v.pList[2], v.pList[1]))), GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(v.pList[1], v.pList[2]))))); */ } else { /* 計算外心 */ triEx = MathEx.GetTriangleExcenterPointF(A, B, C); /* 將點依逆時針方向排序 */ v.pList = MathEx.SortVector(v.pList); for (int i = 0; i < v.pList.Count; i++) { mid = MathEx.GetMidPointF(v.pList[i], v.pList[(i + 1) % 3]); /* 將邊無限延伸的做法 */ v.vList.Add(new Edge(triEx, mid.Add(MathEx.GetNormalVector(v.pList[i], v.pList[(i + 1) % 3]).Multi(600)), v.pList[i], v.pList[(i + 1) % 3])); /* 將邊畫到邊界的做法 * v.vList.Add(new Edge(triEx, GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(v.pList[i], v.pList[(i + 1) % 3]))))); */ } } return(v); }
private void saveFileToolStripMenuItem_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"; saveFileDialog.Title = "Save an Output File"; // if (saveVertices.Count != 0) { this.dataNumber++; Voronoi voronoi = new Voronoi(saveVertices.Count); List <Vertex> sortedVertices = saveVertices.OrderBy(o => o.x).ToList(); sortedVertices = voronoi.y_sorting(sortedVertices); for (int i = 0; i < sortedVertices.Count; i++) { voronoi.setVertex(sortedVertices[i].x, sortedVertices[i].y); } this.voronoiList.Add(voronoi); } if (saveFileDialog.ShowDialog() == DialogResult.OK) { System.IO.StreamWriter writer = new System.IO.StreamWriter(saveFileDialog.OpenFile()); for (int j = 0; j < voronoiList[dataNumber].Num; j++) { writer.WriteLine("P " + voronoiList[dataNumber].getVertex(j).x + " " + voronoiList[dataNumber].getVertex(j).y); } if (voronoiList[dataNumber].HPs.Count != 0) { for (int j = 0; j < voronoiList[dataNumber].HPs.Count; j++) { if (voronoiList[dataNumber].HPs[j].is_line) { writer.WriteLine("E " + voronoiList[dataNumber].HPs[j].start_vertex.x + " " + voronoiList[dataNumber].HPs[j].start_vertex.y + " " + voronoiList[dataNumber].HPs[j].end_vertex.x + " " + voronoiList[dataNumber].HPs[j].end_vertex.y); } if (voronoiList[dataNumber].HPs[j].start_vertex.infinity && voronoiList[dataNumber].HPs[j].end_vertex.infinity) { continue; } writer.WriteLine("E " + voronoiList[dataNumber].HPs[j].start_vertex.x + " " + voronoiList[dataNumber].HPs[j].start_vertex.y + " " + voronoiList[dataNumber].HPs[j].end_vertex.x + " " + voronoiList[dataNumber].HPs[j].end_vertex.y); } } writer.Dispose(); writer.Close(); } canvas.Clear(Color.White); saveVertices.Clear(); voronoiList.Clear(); Es.Clear(); Ps.Clear(); }
public Voronoi VoronoiTwoPoint(PointF A, PointF B) { PointF mid = MathEx.GetMidPointF(A, B); Voronoi v = new Voronoi(); v.pList.Add(A); v.pList.Add(B); /* 將邊無限延伸的做法 */ v.vList.Add(new Edge(mid.Add(MathEx.GetNormalVector(A, B).Multi(600)), mid.Add(MathEx.GetNormalVector(B, A).Multi(600)), A, B)); /* 將邊畫到邊界的做法 * v.vList.Add(new Edge(GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(A, B))), GetSidePointF(mid, mid.Add(MathEx.GetNormalVector(B, A))))); */ return(v); }
public VoronoiDiagramForm() { InitializeComponent(); pictBoxDaigram.AutoSize = true; diagramBitmap = new Bitmap(512, 512); g = Graphics.FromImage(diagramBitmap); g.SmoothingMode = SmoothingMode.HighQuality; g.Clear(Color.Transparent); pen = new Pen(Color.Black) { Width = 2 }; pictBoxDaigram.Image = diagramBitmap; voronoi = new Voronoi(0.1); }
public void BuildVoronoi() { vor = new Voronoi(); vor.Generate(this.points); }
/// <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); }
/// <summary> /// 畫 Voronoi Diagram /// </summary> /// <param name="v">Voronoi 物件</param> /// <param name="color">畫筆顏色</param> public void DrawVoronoiDiagram(Voronoi v, Color color) { DrawVoronoiDiagram(v.vList, color); }
private void Step() { if (record.Count <= 0 || stepIndex > record.Count - 1) { /* 初始化 */ v.pList.Clear(); v.vList.Clear(); record.Clear(); stepIndex = 0; /* 排序點 */ pList = MathEx.SortPointF(pList); /* 畫 Voronoi Diagram */ v = VoronoiMultiPoint(pList); } /* 初始畫布建置 */ ClearPaint(); foreach (PointF i in pList) { DrawPointF(i, Color.DarkRed); } /* 隱藏特定紀錄 */ if (record[stepIndex].type == VORONOI) { if (record[stepIndex].clear) { record[stepIndex - 2].enable = false; int count = 0; for (int i = stepIndex - 1; i >= 0 && count < 2; i--) { if ((record[i].type == MERGE || record[i].type == VORONOI) && record[i].enable == true) { record[i].enable = false; count++; } } } } else if (record[stepIndex].type == CONVEX_HULL) { if (record[stepIndex].clear) { record[stepIndex - 1].enable = false; record[stepIndex - 2].enable = false; } } else if (record[stepIndex].type == MERGE) { if (record[stepIndex].clear) { record[stepIndex - 1].enable = false; record[stepIndex - 2].enable = false; } } /* 畫紀錄 */ for (int i = 0; i <= stepIndex; i++) { if (record[i].enable) { DrawRecord(record[i]); } else { continue; } } /* 重置步驟數 */ if (stepIndex < record.Count) { stepIndex++; } else { stepIndex = 0; } }
private void openFileToolStripMenuItem_Click(object sender, EventArgs e) { this.toolStripButton1.Enabled = false; canvas.Clear(Color.White); voronoiList.Clear(); saveVertices.Clear(); Ps.Clear(); Es.Clear(); this.dataNumber = -1; OpenFileDialog dialog = new OpenFileDialog(); dialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"; dialog.Title = "選擇檔案"; dialog.InitialDirectory = ".\\"; if (dialog.ShowDialog() != DialogResult.OK) { return; } is_open_file = true; int index = 0; string line; //encoding.default避免中文亂碼 System.IO.StreamReader file = new System.IO.StreamReader(dialog.FileName, Encoding.Default); while ((line = file.ReadLine()) != null) { index++; if (line.Contains('#')) { continue; } else { int num = 0; string[] words = line.Split(' '); Voronoi voronoi; if (words[0] == "0") { break; } else if (words[0] != "" && words.Length == 1 && words[0] != "P" && words[0] != "E") { num = Int32.Parse(words[0]); voronoi = new Voronoi(num); for (int i = 0; i < num; i++) { line = file.ReadLine(); string[] points = line.Split(); voronoi.setVertex(Int32.Parse(points[0]), Int32.Parse(points[1])); } this.voronoiList.Add(voronoi); } else if (words[0] == "P") { Vertex tmp_v = new Vertex(); tmp_v.x = Int32.Parse(words[1]); tmp_v.y = Int32.Parse(words[2]); Ps.Add(tmp_v); } else if (words[0] == "E") { Edge tmp_edge = new Edge(); tmp_edge.start_vertex.x = Int32.Parse(words[1]); tmp_edge.start_vertex.y = Int32.Parse(words[2]); tmp_edge.end_vertex.x = Int32.Parse(words[3]); tmp_edge.end_vertex.y = Int32.Parse(words[4]); Es.Add(tmp_edge); } } } this.dataNumber++; //自己點的檔案格式 if (Ps.Count != 0) { Voronoi voronoi = new Voronoi(Ps.Count); for (int i = 0; i < Ps.Count; i++) { voronoi.setVertex(Ps[i].x, Ps[i].y); canvas.FillRectangle(brush, Ps[i].x, Ps[i].y, 2, 2); } voronoiList.Add(voronoi); } else { for (int i = 0; i < voronoiList[dataNumber].Num; i++) { canvas.FillRectangle(brush, voronoiList[dataNumber].getVertex(i).x, voronoiList[dataNumber].getVertex(i).y, 2, 2); } } this.voronoiList[dataNumber].sort(); //若有邊要畫 if (Es.Count != 0) { for (int i = 0; i < Es.Count; i++) { this.canvas.DrawLine(pen, Es[i].start_vertex.x, Es[i].start_vertex.y, Es[i].end_vertex.x, Es[i].end_vertex.y); } } //MessageBox.Show(index.ToString()); this.toolStripStatusLabel1.Text = "Number " + (dataNumber + 1) + " of Data"; if (voronoiList.Count > 1) { this.toolStripButton1.Enabled = true; } input_palette(); this.toolStripButton2.Enabled = true; this.toolStripButton3.Enabled = true; this.pictureBox1.MouseClick -= pictureBox1_MouseClick; file.Dispose(); file.Close(); }