private void LineTo(Vector p) { //Console.WriteLine("LineTo: " + p); if (IntersectingPolygon == null) { IntersectingPolygon = new PolygonModel(); } if (!GeomMath.AlmostEqual(currentStartPoint, p)) { IntersectingPolygon.Lines.Add(new LineModel { StartPoint = new Vector { X = currentStartPoint.X, Y = currentStartPoint.Y }, EndPoint = new Vector { X = p.X, Y = p.Y } }); currentStartPoint = new Vector { X = p.X, Y = p.Y }; //currentStartPoint = p; // point becomes start of next line } }
public void generatePolygon(int which, int nbEdges, float phase, Vector2 dimensions) { if (which<0||which>=polys.Length) return; polys[which] = new PolygonModel(nbEdges,phase,dimensions,nbVertex,1); updateSimpleSplines(which); updateCompositeSplines(which); updateFinalSpline(); }
//public static // Layer 0: Grid points / axis // Layer 1: Input Layer // Layer 2: Input Annotation Layer // Layer 3: Algorithm Layer // Layer 4: Algorithm Annotation Layer public CanvasWrapper(Canvas c, CanvasConfiguration config = null) { AttachedCanvas = c; Layers = new List <CanvasLayer>(); CreateNewLayer("Base Grid Layer"); ActiveLayer = Layers[0]; CreateNewLayer("Input Layer"); CreateNewLayer("Input Annotation Layer"); CreateNewLayer("Algorithm Layer"); CreateNewLayer("Algorithm Annotation Layer"); //Config = new CanvasConfiguration(); Config = config; //Config.BuildConfiguration(-200, 200, -100, 100, // AttachedCanvas.ActualWidth, AttachedCanvas.ActualHeight, // 40, 20, 5, 5); InputPoints = new List <Vector>(); InputLines = new List <LineModel>(); InputPolygons = new List <PolygonModel>(); ActivePolygon = null; DrawGrid(); HookEvents(); CreateDefaultShapes(); }
public void DrawPolygonUsingValues(PolygonModel poly) { foreach (var line in poly.Lines) { DrawLineUsingValues(line.StartPoint.X, line.StartPoint.Y, line.EndPoint.X, line.EndPoint.Y); } }
public void AddPolygonLine(int xi1, int yi1, int xi2, int yi2, double xv1, double yv1, double xv2, double yv2) { if (ActivePolygon == null) { ActivePolygon = new PolygonModel(); } double xp1 = xi1 * Config.XIncrement + Config.XStart; double yp1 = yi1 * Config.YIncrement + Config.YStart; double xp2 = xi2 * Config.XIncrement + Config.XStart; double yp2 = yi2 * Config.YIncrement + Config.YStart; var line = new Line(); line.Stroke = new SolidColorBrush(Colors.Red); line.StrokeThickness = 3; line.Fill = new SolidColorBrush(Colors.Red); line.X1 = xp1; line.Y1 = yp1; line.X2 = xp2; line.Y2 = yp2; ActivePolygon.Lines.Add(new LineModel { StartPoint = new Vector { X = xv1, Y = yv1, Alternates = new CanvasPoint { DotIndexLeft = xi1, DotIndexTop = yi1 } }, EndPoint = new Vector { X = xv2, Y = yv2, Alternates = new CanvasPoint { DotIndexLeft = xi2, DotIndexTop = yi2 } } }); var cc = new CanvasComponent(); cc.AddUiElement(line); line.Tag = ActivePolygon.Lines[ActivePolygon.Lines.Count - 1]; polygonLinesInProgress.Add(cc); GetInputLayer().AddComponent(cc); // if we're at least completing a triangle... if (ActivePolygon.Lines.Count > 2) { // if endpoint of current line matches start point of first line, we're connecting // back to start of polygon if (GeomMath.AlmostEqual(new Vector { X = xv2, Y = yv2 }, ActivePolygon.Lines[0].StartPoint)) { CompletePolygon(); } } }
public void generateRhombus(int which, Vector2 dimensions) { if (which<0||which>=polys.Length) return; polys[which] = new PolygonModel(4,0.0f,dimensions,nbVertex,1); updateSimpleSplines(which); updateCompositeSplines(which); updateFinalSpline(); }
public void generateEllipse(int which, Vector2 dimensions) { if (which<0||which>=polys.Length) return; polys[which] = new PolygonModel(0,0,dimensions,nbVertex,0); updateSimpleSplines(which); polys[which].compositePoly=polys[which].simplePoly; polys[which].compositeGenerated = true; updateFinalSpline(); }
private Polygon GetPolygon(PolygonModel xmlPolygon) { return(new Polygon() { Points = new PointCollection(xmlPolygon.Points), StrokeThickness = xmlPolygon.Stroke, Fill = new SolidColorBrush(xmlPolygon.Color), Stroke = new SolidColorBrush(Colors.Black) }); }
private void CompletePolygon() { if (ActivePolygon == null || !ActivePolygon.IsClosed) { return; } InputPolygons.Add(ActivePolygon); polygonLinesInProgress.Clear(); ActivePolygon = null; }
private RigidBodyModel readRigidBody(OrderedDictionary bodyElem) { RigidBodyModel rbModel = new RigidBodyModel(); rbModel.name = (String)bodyElem["name"]; rbModel.imagePath = (String)bodyElem["imagePath"]; var bodyElemObject = bodyElem["origin"] as JObject; OrderedDictionary originElem = bodyElemObject.ToObject <OrderedDictionary>(); rbModel.origin.X = Convert.ToSingle(originElem["x"]); rbModel.origin.Y = Convert.ToSingle(originElem["y"]); // polygons var bEA = bodyElem["polygons"] as JArray; for (int i = 0; i < bEA.Count; i++) { PolygonModel polygon = new PolygonModel(); rbModel.polygons.Add(polygon); var verticesElem = bEA[i] as JArray; for (int ii = 0; ii < verticesElem.Count; ii++) { OrderedDictionary vertexElem = verticesElem[ii].ToObject <OrderedDictionary>(); float x = Convert.ToSingle(vertexElem["x"]); float y = Convert.ToSingle(vertexElem["y"]); polygon.vertices.Add(new Vector2(x, y)); } polygon.buffer = new Vertices(polygon.vertices.Count); } // circles var circlesElem = bodyElem["circles"] as JArray; for (int i = 0; i < circlesElem.Count; i++) { CircleModel circle = new CircleModel(); rbModel.circles.Add(circle); OrderedDictionary circleElem = circlesElem[i].ToObject <OrderedDictionary>(); circle.center.X = (float)circleElem["cx"]; circle.center.Y = (float)circleElem["cy"]; circle.radius = (float)circleElem["r"]; } return(rbModel); }
public IActionResult InsertPolygon(string values) { var newPolygon = new PolygonModel(); JsonConvert.PopulateObject(values, newPolygon); if (!TryValidateModel(newPolygon)) { return(BadRequest(ModelState.GetFullErrorMessage())); } polygons.Add(newPolygon); return(Ok(newPolygon)); }
//public BodyEditorLoader(string str) { // if (str == null) throw new NullReferenceException("str is null"); // model = readJson(str); //} // ------------------------------------------------------------------------- // Public API // ------------------------------------------------------------------------- /** * Creates and applies the fixtures defined in the editor. The name * parameter is used to retrieve the right fixture from the loaded file. * <br/><br/> * * The body reference point (the red cross in the tool) is by default * located at the bottom left corner of the image. This reference point * will be put right over the BodyDef position point. Therefore, you should * place this reference point carefully to let you place your body in your * world easily with its BodyDef.position point. Note that to draw an image * at the position of your body, you will need to know this reference point * (see {@link #getOrigin(java.lang.String, float)}. * <br/><br/> * * Also, saved shapes are normalized. As shown in the tool, the width of * the image is considered to be always 1 meter. Thus, you need to provide * a scale factor so the polygons get resized according to your needs (not * every body is 1 meter large in your game, I guess). * * @param body The Box2d body you want to attach the fixture to. * @param name The name of the fixture you want to load. * @param fd The fixture parameters to apply to the created body fixture. * @param scale The desired scale of the body. The default width is 1. */ public void attachFixture(Body body, String name, float scale) { // deleted FixtureDef RigidBodyModel rbModel = model.rigidBodies[name]; if (rbModel == null) { throw new SystemException("Name '" + name + "' was not found."); } vec = rbModel.origin * scale; Vector2 origin = vec; for (int i = 0, n = rbModel.polygons.Count; i < n; i++) { PolygonModel polygon = rbModel.polygons[i]; Vertices vertices = new Vertices(polygon.vertices); for (int ii = 0, nn = vertices.Count; ii < nn; ii++) { var v = NewVec(); v = vertices[ii] * scale; vertices[ii] = v; vertices[ii] -= origin; } polygonShape.Set(vertices); body.CreateFixture(polygonShape); for (int ii = 0, nn = vertices.Count; ii < nn; ii++) { Free(vertices[ii]); } } for (int i = 0, n = rbModel.circles.Count; i < n; i++) { CircleModel circle = rbModel.circles[i]; var v2 = NewVec(); v2 = circle.center * scale; Vector2 center = v2; float radius = circle.radius * scale; circleShape.Position = center; circleShape.Radius = radius; body.CreateFixture(circleShape); Free(center); } }
public void CancelActivePolygon() { if (ActivePolygon == null) { return; } foreach (var cc in polygonLinesInProgress) { GetInputLayer().RemoveComponent(cc); } polygonLinesInProgress.Clear(); ActivePolygon = null; }
public void setArbitraryPolygon(int which, Vector2[] arbitraryPolygon) { if (which<0||which>=polys.Length) return; Vector2 bottomLeft = arbitraryPolygon[0]; Vector2 topRight = arbitraryPolygon[0]; for (int i=0;i<arbitraryPolygon.Length;i++) { bottomLeft.x = Mathf.Min(arbitraryPolygon[i].x,bottomLeft.x); bottomLeft.y = Mathf.Min(arbitraryPolygon[i].y,bottomLeft.y); topRight.x = Mathf.Max(arbitraryPolygon[i].x,topRight.x); topRight.y = Mathf.Max(arbitraryPolygon[i].y,topRight.y); } polys[which] = new PolygonModel(arbitraryPolygon.Length,0,new Vector2(topRight.x-bottomLeft.x,topRight.y-bottomLeft.y), nbVertex, 1); polys[which].arbitrary = true; polys[which].simplePoly = arbitraryPolygon; updateCompositeSplines(which); updateFinalSpline(); }
/// <summary> /// Deserialize chosen polygon /// </summary> /// <param name="xmlPolygon"></param> /// <returns>Polygon</returns> public Polygon GetPolygon(PolygonModel xmlPolygon) { if (xmlPolygon != null) { return(new Polygon() { Points = new PointCollection(xmlPolygon.Points), StrokeThickness = xmlPolygon.Stroke, Fill = new SolidColorBrush(xmlPolygon.Color), Stroke = new SolidColorBrush(Colors.Black) }); } else { throw new ArgumentException("There was no Polygon to get."); } }
public void PolygonService_GetPolygon_Test() { Point Point1 = new Point(1, 50); Point Point2 = new Point(10, 80); Point Point3 = new Point(50, 50); List <Point> pointsList = new List <Point>(); pointsList.Add(Point1); pointsList.Add(Point2); pointsList.Add(Point3); Color color = new Color(); color = Color.FromRgb(255, 0, 0); PolygonModel pm = new PolygonModel(pointsList, color, 1); PolygonsService ps = new PolygonsService(); Polygon myPolygon = ps.GetPolygon(pm); Assert.AreEqual(myPolygon.StrokeThickness, 1); }
private void DrawPolygons(PolygonModel polygon) { if (polygon == null) { return; } GL.Begin(PrimitiveType.Triangles); for (int l = 0; l < polygon.Faces.Count; l++) { var normal = polygon.Faces[l].Normal(); GL.Color4(Math.Abs(normal.X), Math.Abs(normal.Y), Math.Abs(normal.Z), 0); GL.Normal3(N2TK(normal)); GL.Vertex3(N2TK(polygon.Faces[l].Vertices[0].P)); GL.Vertex3(N2TK(polygon.Faces[l].Vertices[2].P)); GL.Vertex3(N2TK(polygon.Faces[l].Vertices[1].P)); } GL.End(); }
public void PolygonModel_Test() { PolygonModel polygon1 = new PolygonModel(); Point Point1 = new Point(1, 50); Point Point2 = new Point(10, 80); Point Point3 = new Point(50, 50); List <Point> points_arr = new List <Point>(); points_arr.Add(Point1); points_arr.Add(Point2); points_arr.Add(Point3); Color color = new Color(); color = Color.FromRgb(255, 0, 0); PolygonModel polygon2 = new PolygonModel(points_arr, color, 2); polygon1.Stroke = 2; Assert.AreNotEqual(3, polygon1.Stroke); Assert.AreEqual(3, polygon2.Points.Count); Assert.AreEqual(2, polygon2.Stroke); Assert.AreEqual(color, polygon2.Color); }
public void Render(PolygonModel polygon) { Polygon = polygon; // clear buffer GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // set camera setting Vector3 vec_rotate = new Vector3((float)rotateX, (float)rotateY, (float)rotateZ); Vector3 center = new Vector3(N2TK(Polygon.GravityPoint())); Vector3 eye = center + vec_rotate * center.LengthFast / zoom; Matrix4 modelView = Matrix4.LookAt(eye, center, Vector3.UnitY); // set disp mode GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelView); // display shape model DrawPolygons(polygon); glControl.SwapBuffers(); }
public override void Run() { // Algorithm Setup: save input points var layer = History.CreateAndAddNewLayer("Setup (sorting input)"); layer.AddCommand(new UpdatePointSetCommand { Label = "Input", Points = AlgorithmUtil.CopyVectorList(InputPoints) }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_SORT_START, SECTION_SORT)); SortedInput = new List <Vector>(); // algorithm step 1: sort, saved sorted input points foreach (var v in InputPoints) { SortedInput.Add(new Vector { X = v.X, Y = v.Y, Alternates = v.Alternates }); } SortedInput.Sort(GrahamSort); layer.AddCommand(new UpdatePointSetCommand { Label = "Sorted Input", Points = AlgorithmUtil.CopyVectorList(SortedInput) }); // check degenerate case #1: 0,1,2 points layer = History.CreateAndAddNewLayer("Degenerate Case Checks"); if (SortedInput.Count <= 2) { layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Algorithm cannot execute, it requires at least 3 points" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_DEGENERATE)); return; } // check degenerate case #2: all points on same line if (AllPointsCollinear()) { layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Algorithm cannot execute if all points are collinear" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_DEGENERATE)); return; } // points cannot be collinear, points must be at least 3 Stack <Vector> grahamStack = new Stack <Vector>(); grahamStack.Push(SortedInput[0]); grahamStack.Push(SortedInput[1]); // starting points: first two sorted points, pushed onto stack // STATIC LAYER: "Starting Points", has only input points // layer commentary: if 0, 1 or 2 points, cannot perform graham scan // if all points on same line, cannot perform graham scan layer = History.CreateAndAddNewLayer("Initiailzation"); layer.AddCommand(new AlgorithmStartingCommand { AssociatedAlgorithm = this }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_PRE)); var hip = new HighlightPointsCommand { AssociatedAlgorithm = this, HighlightLevel = 1 }; hip.Points.Add(new Vector { X = SortedInput[0].X, Y = SortedInput[0].Y }); hip.Points.Add(new Vector { X = SortedInput[1].X, Y = SortedInput[1].Y }); layer.AddCommand(hip); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Initial stack" }); AddStackLinesToLayer(layer, grahamStack); for (int i = 2; i < SortedInput.Count; i++) { layer = History.CreateAndAddNewLayer("Processing Point, index=" + i); layer.AddCommand(new AlgorithmStepStartingCommand { AssociatedAlgorithm = this, Comments = "Step Starting" }); layer.AddCommand(new ClearPointHighlightsCommand { AssociatedAlgorithm = this, Comments = "" }); layer.AddCommand(new ClearTextStatusCommand { AssociatedAlgorithm = this, Comments = "" }); // NORMAL LAYER: "Algorithm Layer (i-2)" // Commentary: "We examine the next point in sorted order with the top two // elements from the stack status structure, popping the first one" // stack visualization: highlight top of stack, label it "tail" // standalone visualization: show SortedInput[i] as "head" // show popped element as "middle" // highlight the "head" point in yellow, and the "middle" / "tail" points in green // layer commentary: // loop iteration "i" Vector head = SortedInput[i]; Vector middle = grahamStack.Pop(); Vector tail = grahamStack.Peek(); layer.AddCommand(new HighlightInputPointCommand { AssociatedAlgorithm = this, Comments = "Next input point", X = head.X, Y = head.Y, HightlightLevel = 1 }); hip = new HighlightPointsCommand { AssociatedAlgorithm = this, HighlightLevel = 1 }; hip.Points.Add(new Vector { X = head.X, Y = head.Y }); hip.Points.Add(new Vector { X = middle.X, Y = middle.Y }); hip.Points.Add(new Vector { X = tail.X, Y = tail.Y }); layer.AddCommand(hip); layer.AddCommand(new HighlightInputPointCommand { AssociatedAlgorithm = this, Comments = "Freshly popped from top of stack", X = middle.X, Y = middle.Y, HightlightLevel = 2 }); layer.AddCommand(new HighlightInputPointCommand { AssociatedAlgorithm = this, Comments = "Top of stack, which is left on stack and peeked", X = tail.X, Y = tail.Y, HightlightLevel = 2 }); // we examine next point in sorted list, with top element of stack (which we pop) // and 2nd element of stack (which we leave as new top of stack) // Commentary: "The turn direction of these three points is calculated using // the cross product of these three points" int turn = GeomMath.GetTurnDirection(tail, middle, head); // determine "turn" of these 3 points, in sequence tail / middle / head string turnString = "Counter-clockwise"; if (turn == GeomMath.DIRECTION_CLOCKWISE) { turnString = "Clockwise"; } else if (turn == GeomMath.DIRECTION_NONE) { turnString = "None"; } layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Computed turn: " + turnString }); // Standalone visualization: "The turn direction for these three points is: " switch (turn) { case GeomMath.DIRECTION_COUNTERCLOCKWISE: layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The point popped is pushed back onto stack since it is part of the hull" }); layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The input point is also pushed since it is potentially part of the hull" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_COUNTERCLOCKWISE)); grahamStack.Push(middle); grahamStack.Push(head); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Updated processing stack" }); break; case GeomMath.DIRECTION_CLOCKWISE: layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The point on the top of the stack is discarded, but the input point is preserved for re-consideration" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_CLOCKWISE)); i--; break; case GeomMath.DIRECTION_NONE: layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Input point is co-linear with other points, so it is part of the hull" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_NONE)); grahamStack.Push(head); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Updated processing stack" }); break; } AddStackLinesToLayer(layer, grahamStack); } layer = History.CreateAndAddNewLayer("Final Results"); layer.AddCommand(new AlgorithmCompleteCommand { AssociatedAlgorithm = this }); layer.AddCommand(new ClearPointHighlightsCommand { AssociatedAlgorithm = this, Comments = "" }); layer.AddCommand(new ClearTextStatusCommand { AssociatedAlgorithm = this, Comments = "" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_POST)); grahamStack.Push(SortedInput[0]); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Final stack, first input point is pushed to complete the hull" }); AddStackLinesToLayer(layer, grahamStack); Hull = new PolygonModel(); var a = grahamStack.ToArray <Vector>(); for (int i = 0; i < a.Length - 1; i++) { var ap = a[i].Alternates; var ap2 = a[i + 1].Alternates; if (ap != null && ap2 != null) { // Main operation Hull.Lines.Add(new LineModel { StartPoint = new Vector { X = a[i].X, Y = a[i].Y, Alternates = new CanvasPoint { DotIndexLeft = ap.DotIndexLeft, DotIndexTop = ap.DotIndexTop } }, EndPoint = new Vector { X = a[i + 1].X, Y = a[i + 1].Y, Alternates = new CanvasPoint { DotIndexLeft = ap2.DotIndexLeft, DotIndexTop = ap2.DotIndexTop } } }); } else { // Side operation that doesn't involve points from grid Hull.Lines.Add(new LineModel { StartPoint = new Vector { X = a[i].X, Y = a[i].Y }, EndPoint = new Vector { X = a[i + 1].X, Y = a[i + 1].Y } }); } } }
//public Delaunator(IEnumerable<DelaunatorPoint> points) public override void Run() { if (InputPoints.Count() < 2) { throw new ArgumentOutOfRangeException("Need at least 3 points"); } //Points = points.ToList(); coords = new double[InputPoints.Count * 2]; for (var i = 0; i < InputPoints.Count; i++) { var p = InputPoints.ElementAtOrDefault(i); coords[2 * i] = p.X; coords[2 * i + 1] = p.Y; } var n = coords.Length >> 1; var maxTriangles = 2 * n - 5; Triangles = new int[maxTriangles * 3]; Halfedges = new int[maxTriangles * 3]; hashSize = (int)Math.Ceiling(Math.Sqrt(n)); hullPrev = new int[n]; hullNext = new int[n]; hullTri = new int[n]; hullHash = new int[hashSize]; var ids = new int[n]; var minX = double.PositiveInfinity; var minY = double.PositiveInfinity; var maxX = double.NegativeInfinity; var maxY = double.NegativeInfinity; for (var i = 0; i < n; i++) { var x = coords[2 * i]; var y = coords[2 * i + 1]; if (x < minX) { minX = x; } if (y < minY) { minY = y; } if (x > maxX) { maxX = x; } if (y > maxY) { maxY = y; } ids[i] = i; } var cx = (minX + maxX) / 2; var cy = (minY + maxY) / 2; var minDist = double.PositiveInfinity; int i0 = 0, i1 = 0, i2 = 0; // pick a seed point close to the center for (int i = 0; i < n; i++) { var d = Dist(cx, cy, coords[2 * i], coords[2 * i + 1]); if (d < minDist) { i0 = i; minDist = d; } } var i0x = coords[2 * i0]; var i0y = coords[2 * i0 + 1]; minDist = double.PositiveInfinity; // find the point closest to the seed for (int i = 0; i < n; i++) { if (i == i0) { continue; } var d = Dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]); if (d < minDist && d > 0) { i1 = i; minDist = d; } } var i1x = coords[2 * i1]; var i1y = coords[2 * i1 + 1]; var minRadius = double.PositiveInfinity; // find the third point which forms the smallest circumcircle with the first two for (int i = 0; i < n; i++) { if (i == i0 || i == i1) { continue; } var r = Circumradius(i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]); if (r < minRadius) { i2 = i; minRadius = r; } } var i2x = coords[2 * i2]; var i2y = coords[2 * i2 + 1]; if (minRadius == double.PositiveInfinity) { throw new Exception("No Delaunay triangulation exists for this input."); } if (Orient(i0x, i0y, i1x, i1y, i2x, i2y)) { var i = i1; var x = i1x; var y = i1y; i1 = i2; i1x = i2x; i1y = i2y; i2 = i; i2x = x; i2y = y; } var center = Circumcenter(i0x, i0y, i1x, i1y, i2x, i2y); _cx = center.X; _cy = center.Y; var dists = new double[n]; for (var i = 0; i < n; i++) { dists[i] = Dist(coords[2 * i], coords[2 * i + 1], center.X, center.Y); } // sort the points by distance from the seed triangle circumcenter Quicksort(ids, dists, 0, n - 1); // set up the seed triangle as the starting hull hullStart = i0; hullSize = 3; hullNext[i0] = hullPrev[i2] = i1; hullNext[i1] = hullPrev[i0] = i2; hullNext[i2] = hullPrev[i1] = i0; hullTri[i0] = 0; hullTri[i1] = 1; hullTri[i2] = 2; hullHash[HashKey(i0x, i0y)] = i0; hullHash[HashKey(i1x, i1y)] = i1; hullHash[HashKey(i2x, i2y)] = i2; trianglesLen = 0; AddTriangle(i0, i1, i2, -1, -1, -1); double xp = 0; double yp = 0; for (var k = 0; k < ids.Length; k++) { var i = ids[k]; var x = coords[2 * i]; var y = coords[2 * i + 1]; // skip near-duplicate points if (k > 0 && Math.Abs(x - xp) <= EPSILON && Math.Abs(y - yp) <= EPSILON) { continue; } xp = x; yp = y; // skip seed triangle points if (i == i0 || i == i1 || i == i2) { continue; } // find a visible edge on the convex hull using edge hash var start = 0; for (var j = 0; j < hashSize; j++) { var key = HashKey(x, y); start = hullHash[(key + j) % hashSize]; if (start != -1 && start != hullNext[start]) { break; } } start = hullPrev[start]; var e = start; var q = hullNext[e]; while (!Orient(x, y, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1])) { e = q; if (e == start) { e = int.MaxValue; break; } q = hullNext[e]; } if (e == int.MaxValue) { continue; // likely a near-duplicate point; skip it } // add the first triangle from the point var t = AddTriangle(e, i, hullNext[e], -1, -1, hullTri[e]); // recursively flip triangles from the point until they satisfy the Delaunay condition hullTri[i] = Legalize(t + 2); hullTri[e] = t; // keep track of boundary triangles on the hull hullSize++; // walk forward through the hull, adding more triangles and flipping recursively var next = hullNext[e]; q = hullNext[next]; while (Orient(x, y, coords[2 * next], coords[2 * next + 1], coords[2 * q], coords[2 * q + 1])) { t = AddTriangle(next, i, q, hullTri[i], -1, hullTri[next]); hullTri[i] = Legalize(t + 2); hullNext[next] = next; // mark as removed hullSize--; next = q; q = hullNext[next]; } // walk backward from the other side, adding more triangles and flipping if (e == start) { q = hullPrev[e]; while (Orient(x, y, coords[2 * q], coords[2 * q + 1], coords[2 * e], coords[2 * e + 1])) { t = AddTriangle(q, i, e, -1, hullTri[e], hullTri[q]); Legalize(t + 2); hullTri[q] = t; hullNext[e] = e; // mark as removed hullSize--; e = q; q = hullPrev[e]; } } // update the hull indices hullStart = hullPrev[i] = e; hullNext[e] = hullPrev[next] = i; hullNext[i] = next; // save the two new edges in the hash table hullHash[HashKey(x, y)] = i; hullHash[HashKey(coords[2 * e], coords[2 * e + 1])] = e; } hull = new int[hullSize]; var s = hullStart; for (var i = 0; i < hullSize; i++) { hull[i] = s; s = hullNext[s]; } hullPrev = hullNext = hullTri = null; // get rid of temporary arrays //// trim typed triangle mesh arrays Triangles = Triangles.Take(trianglesLen).ToArray(); Halfedges = Halfedges.Take(trianglesLen).ToArray(); var layer = History.CreateAndAddNewLayer("Final Result"); //AddEdgesToLayer(layer); if (!voronoiOperation) { foreach (var edge in GetEdges()) { var v1 = edge.P; var v2 = edge.Q; AddLineCommand(layer, v1, v2); } } else { //foreach (var edge in GetVoronoDelaunatorEdges()) //{ // var v1 = edge.P; // var v2 = edge.Q; // AddNonIndexedLineCommand(layer, v1, v2); //} foreach (var cell in GetVoronoiCells()) { var poly = new PolygonModel(); //foreach (var p in cell.Points) for (int i = 0; i < cell.Points.Count; i++) { var sp = cell.Points[i]; var np = cell.Points[(i + 1) % cell.Points.Count]; poly.Lines.Add(new LineModel { StartPoint = new Vector { X = sp.X, Y = sp.Y }, EndPoint = new Vector { X = np.X, Y = np.Y } }); } AddNonIndexedPolygonCommand(layer, poly); } } }