public static void addToQueue(FlatQueue <float> queue, DelaunatorSharp.Delaunator delauny , DelaunatorSharp.IPoint[] points, Circumcircles circumcircles, bool[] onEdge, bool[] visited, int i) { onEdge[i] = true; visited[delauny.Triangles[i]] = true; DelaunatorSharp.IPoint p0 = points[delauny.Triangles[i]]; DelaunatorSharp.IPoint p1 = points[delauny.Triangles[i % 3 == 2 ? i - 2 : i + 1]]; float r = circumcircles.r[i]; float x = circumcircles.x[i]; float y = circumcircles.y[i]; float x0 = (float)p0.X; float y0 = (float)p0.Y; float x1 = (float)p1.X; float y1 = (float)p1.Y; // var [x0, y0] = p0; // var[x1, y1] = p1; float area = (y - y0) * (x1 - x) - (x - x0) * (y1 - y); float cx = (x0 + x1) / 2; float cy = (y0 + y1) / 2; float d = (float)System.Math.Sqrt(System.Math.Pow(cx - x, 2) + System.Math.Pow(cy - y, 2)); // if the center of a circumcircle on the edge lies outside the advancing polygon, // or inside but less than 0.2 circumradiuses away from the edge, queue it for collapsing if (area >= 0f || d / r < 0.2f) { queue.Push(i, -r); } }
// https://observablehq.com/@mourner/adaptive-concave-hull // https://mapbox.github.io/delaunator/ public static void ComputeHull() { string jsonFile = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "DelaunatorPoints.json"); jsonFile = System.IO.Path.GetFullPath(jsonFile); string json = System.IO.File.ReadAllText(jsonFile, System.Text.Encoding.UTF8); // DelaunatorSharp.IPoint[] points = Newtonsoft.Json.JsonConvert.DeserializeObject<DelaunatorSharp.IPoint[]>(json); DelaunatorSharp.IPoint[] points = Newtonsoft.Json.JsonConvert.DeserializeObject <DelaunatorSharp.Point[]>(json).Cast <DelaunatorSharp.IPoint>().ToArray(); points = EnhancePointDensity(points, 10); // string json = Newtonsoft.Json.JsonConvert.SerializeObject(points); // System.IO.File.WriteAllText(jsonFile, json,System.Text.Encoding.UTF8); BoundingBox bbox = GetBbox(points); // float width = bbox.maxX - bbox.minX; float width = bbox.maxX * 6; float padding = (float)System.Math.Round(width * 0.02); float scale = (width - 2 * padding) / (bbox.maxX - bbox.minX); float height = (float)System.Math.Ceiling(scale * (bbox.maxY - bbox.minY)) + 2 * padding; width = width + 2 * padding; DelaunatorSharp.Delaunator delauny = new DelaunatorSharp.Delaunator(points); Circumcircles circumcircles = ComputeCircumcircles(points, delauny); FlatQueue <float> queue = new FlatQueue <float>(); bool[] onEdge = new bool[delauny.Halfedges.Length]; bool[] visited = new bool[points.Length]; for (int i = 0; i < delauny.Halfedges.Length; i++) { if (delauny.Halfedges[i] == -1) { addToQueue(queue, delauny, points, circumcircles, onEdge, visited, i); // start with convex hull edges } } // var ctx = DOM.context2d(width, height); int imageCount = 0; System.Drawing.Bitmap bmp = new System.Drawing.Bitmap((int)width, (int)height); using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp)) { // Draw(delauny, points, bbox, circumcircles, g, padding, scale, queue, onEdge); // bmp.Save(@"D:\TestImage" + imageCount + ".png", System.Drawing.Imaging.ImageFormat.Png); // imageCount++; while (true) { int?i = queue.Pop(); if (i == null) { break; } int i1 = delauny.Halfedges[i.Value % 3 == 2 ? i.Value - 2 : i.Value + 1]; int i2 = delauny.Halfedges[i.Value % 3 == 0 ? i.Value + 2 : i.Value - 1]; if (i1 != -1 && i2 != -1 && !visited[delauny.Triangles[i1]]) { addToQueue(queue, delauny, points, circumcircles, onEdge, visited, i1); addToQueue(queue, delauny, points, circumcircles, onEdge, visited, i2); onEdge[i.Value] = false; } // yield draw(ctx, queue, onEdge); // Draw(delauny, points, bbox, circumcircles, g, padding, scale, queue, onEdge); // if(System.Environment.OSVersion.Platform == PlatformID.Unix) // bmp.Save(@"TestImage" + imageCount + ".png", System.Drawing.Imaging.ImageFormat.Png); // else // bmp.Save(@"D:\TestImage" + imageCount + ".png", System.Drawing.Imaging.ImageFormat.Png); // imageCount++; } // Whend Draw(delauny, points, bbox, circumcircles, g, padding, scale, queue, onEdge); if (System.Environment.OSVersion.Platform == System.PlatformID.Unix) { bmp.Save(@"TestImage.png", System.Drawing.Imaging.ImageFormat.Png); } else { bmp.Save(@"D:\TestImage.png", System.Drawing.Imaging.ImageFormat.Png); } } // End Using g System.Console.Write("finished"); }
public static void Draw( DelaunatorSharp.Delaunator delaunay , DelaunatorSharp.IPoint[] points , BoundingBox bbox , Circumcircles circumcircles , System.Drawing.Graphics ctx , float padding , float scale , FlatQueue <float> queue , bool[] onEdge ) { // ctx.clearRect(0, 0, width, height); ctx.Clear(System.Drawing.Color.White); System.Drawing.Drawing2D.GraphicsPath path1 = new System.Drawing.Drawing2D.GraphicsPath(); int[] t = delaunay.Triangles; for (int i = 0; i < t.Length; i += 3) { DelaunatorSharp.IPoint pt1 = points[t[i + 0]]; float ax = (float)pt1.X; float ay = (float)pt1.Y; DelaunatorSharp.IPoint pt2 = points[t[i + 1]]; float bx = (float)pt2.X; float by = (float)pt2.Y; DelaunatorSharp.IPoint pt3 = points[t[i + 2]]; float cx = (float)pt3.X; float cy = (float)pt3.Y; path1.StartFigure(); path1.AddLine(projX(ax, padding, scale, bbox), projY(ay, padding, scale, bbox), projX(bx, padding, scale, bbox), projY(by, padding, scale, bbox)); path1.AddLine(projX(bx, padding, scale, bbox), projY(by, padding, scale, bbox), projX(cx, padding, scale, bbox), projY(cy, padding, scale, bbox)); path1.CloseFigure(); } System.Drawing.Pen trianglePen = new System.Drawing.Pen(System.Drawing.Color.FromArgb((int)(0.4 * 255), 0, 200, 0)); trianglePen.Width = 0.5f; trianglePen.Alignment = System.Drawing.Drawing2D.PenAlignment.Center; ctx.DrawPath(trianglePen, path1); System.Drawing.Drawing2D.GraphicsPath path2 = new System.Drawing.Drawing2D.GraphicsPath(); // for (const [x, y] of points) foreach (DelaunatorSharp.IPoint thisPoint in points) { float sx = projX((float)thisPoint.X, padding, scale, bbox); float sy = projY((float)thisPoint.Y, padding, scale, bbox); float r = 1.5f; r = 5; path2.StartFigure(); path2.AddArc(sx - r / 2.0f, sy - r / 2.0f, r, r, 0.0f, 360.0f); path2.CloseFigure(); } System.Drawing.Brush blackBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black); ctx.FillPath(blackBrush, path2); // ctx.beginPath(); System.Drawing.Drawing2D.GraphicsPath path3 = new System.Drawing.Drawing2D.GraphicsPath(); // path3.StartFigure(); for (int i = 0; i < onEdge.Length; i++) { if (!onEdge[i]) { continue; } DelaunatorSharp.IPoint pt1 = points[t[i]]; float ax = (float)pt1.X; float ay = (float)pt1.Y; DelaunatorSharp.IPoint pt2 = points[t[i % 3 == 2 ? i - 2 : i + 1]]; float bx = (float)pt2.X; float by = (float)pt2.Y; path3.StartFigure(); path3.AddLine(projX(ax, padding, scale, bbox), projY(ay, padding, scale, bbox), projX(bx, padding, scale, bbox), projY(by, padding, scale, bbox)); path3.CloseFigure(); // ctx.moveTo(projX(ax), projY(ay)); // ctx.lineTo(projX(bx), projY(by)); // ctx.closePath(); } // path3.CloseFigure(); System.Drawing.Pen hullPen = new System.Drawing.Pen(System.Drawing.Color.Blue); hullPen.Width = 2.0f; ctx.DrawPath(hullPen, path3); System.Drawing.Drawing2D.GraphicsPath path4 = new System.Drawing.Drawing2D.GraphicsPath(); foreach (int i in queue.ids) { // ctx.beginPath(); path4.StartFigure(); float sr = circumcircles.r[i] * scale; float sx = projX(circumcircles.x[i], padding, scale, bbox); float sy = projY(circumcircles.y[i], padding, scale, bbox); //ctx.moveTo(sx + sr, sy); //ctx.arc(sx, sy, sr, 0, Math.PI* 2, false); //ctx.strokeStyle = 'rgba(200,0,0,1)'; //ctx.lineWidth = 1; //ctx.stroke(); //ctx.fillStyle = 'rgba(255,255,0,0.2)'; //ctx.fill(); path4.AddArc(sx - sr / 2.0f, sy - sr / 2.0f, sr, sr, 0.0f, 360.0f); path4.CloseFigure(); } System.Drawing.SolidBrush circleBrush = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb((int)(0.2 * 255), 255, 255, 0)); System.Drawing.Pen redCircle = new System.Drawing.Pen(System.Drawing.Color.FromArgb(255, 200, 0, 0)); redCircle.Width = 1; redCircle.Alignment = System.Drawing.Drawing2D.PenAlignment.Center; ctx.FillPath(circleBrush, path4); ctx.DrawPath(redCircle, path4); }