stmoong Posted September 6, 2010 Posted September 6, 2010 (edited) I'm trying to split a polygon into 3 separate pieces using the GetSplitCurves(Point3dCollection) method. The original polygon looks like this. It is a Polyline object and is closed. I pass in the Point3dCollection which consists of the points A, B, C and D, in that order. When I check the vertices of the returned entities, I get 2 polygons instead of 3, and the vertices are not in the right order: May I know if it's possible to pass in the 4 coordianates so that when the polygon is split, we get 3 polygons - the center polygon would be A, B, C, D, the other two polygons will be the two sides? Here's my code snippet: List<custom_object> myPolygons = new List<custom_object>(); DBObjectCollection acDboCol = acPoly.GetSplitCurves(acPt3dCol); foreach (Entity acEnt in acDboCol) { Polyline acNewPLine = acEnt as Polyline; acNewPLine.Closed = true; List<Point3d> acVertices = new List<Point3d>(); for (int i = 0; i < acNewPLine.NumberOfVertices; i++) { acVertices.Add(acNewPLine.GetPoint3dAt(i)); } myPolygons.Add(acVertices); acNewPLine.Dispose(); } // Dispose of DBObject derived types acPoly.Dispose(); acDboCol.Dispose(); Edited September 6, 2010 by stmoong Image order was wrong. Quote
SEANT Posted September 6, 2010 Posted September 6, 2010 Is it possible to isolate the splits? Only add points A & B to acPt3dCol, GetSplitCurves, Close each entity in acDboCol. Then Clear acPt3dCol and add C & D, GetSplitCurves on the curves in acDboCol, . . . . The one issue is that C & D will only fall on one of the two closed polys from the first iteration. That may not be a problem unless speed optimization is a major concern. Quote
stmoong Posted September 7, 2010 Author Posted September 7, 2010 (edited) Yup, I tried that method as well, and getting strange result. I just want to clarify that all these are done as background computation, i.e. the application is not actually drawing the entities, the Polyline instances are used purely to get the vertices of the split polygons. I call the GetSplitCurves, passing in 3dPointCollection (consisting of A, B). In the loop, I do the casting of Entity to Polyline and set the Closed to true. In the first iteration of main loop, I call another GetSplitCurves on Polyline (A, 2, 3, B), passing in 3dPointCollection (consisting of C, D). In my case, it throws an Autodesk.AutoCAD.Runtime.Exception with the ErrorStatus == InvalidInput, so I ignore it and just store the result anyway, as it seems to be a case where I try to split the polyline with another polyline that is outside. Then, in the second iteration, calling the GetSplitCurves method on Polyline ( A, B, 4, 1) with 3dPointCollection (consisting of C, D) returns 1 Entity with 8 vertices. This Entity is a Polyline with the vertex order (D, 4, 1, A, B, 4, 1, C). I'm not sure if it's due to the double data type or is it because I need to save the database transaction or something? I got those intersection points by using a method Line2d.GetPerpendicularLine(), passing in B and D as input parameters to get the corresponding A and C, and store them inside Point3dCollection. Edited September 7, 2010 by stmoong Clarification on using Polyline Quote
SEANT Posted September 7, 2010 Posted September 7, 2010 (edited) Here is the test code (from the previous thread) modified as an example of “Isolating the splits”. It does require a convoluted looping structure to make sure everything takes place at the proper time. It seems to work, though the routine has withstood very little testing. With regard to DatabaseServices.Curve ó Geometry.Curve2d/3d, the routine above is flirting with the margin. Any geometric manipulation more complex than this would favor the Geometry namespace, in my opinion. Edit: Faulty code deleted! Edited September 8, 2010 by SEANT Buggy code deleted Quote
stmoong Posted September 7, 2010 Author Posted September 7, 2010 Here's my code snippet from previous testing. Looks pretty similar. I am guessing I need to somehow reorder the first segment of the split polygon so that the starting point of the second splitting polyline lies on that segment. I might be wrong, but it looks to me as though the GetSplitCurves() algorithm is going by a clockwise order and then split up the polygon. Still working on the reordering algo. Ugly code, will have to refactor later once I get it working. I thought of using the Geometry namespace, but couldn't figure out how to get the split polygons. It might result in cleaner code. List<MyCustomObject> myCustomObjList = new List<MyCustomObject>(); DBObjectCollection acDboCol_1 = null; try { acDboCol_1 = acPoly.GetSplitCurves(acPt3dCol_1); foreach (Entity acEnt in acDboCol_1) { Polyline acNewPLine = acEnt as Polyline; acNewPLine.Closed = true; DBObjectCollection acDboCol_2 = null; try { acDboCol_2 = acNewPLine.GetSplitCurves(acPt3dCol_2); foreach (Entity acEnt2 in acDboCol_2) { Polyline acNewPLine2 = acEnt2 as Polyline; acNewPLine2.Closed = true; MyCustomObject myCustomObj = new MyCustomObject(); myCustomObj.Vertices = new List<Point3d>(); for (int i = 0; i < acNewPLine2.NumberOfVertices; i++) { myCustomObj.Vertices.Add(acNewPLine2.GetPoint3dAt(i)); } myCustomObjList.Add(myCustomObj); acNewPLine2.Dispose(); } } catch (Autodesk.AutoCAD.Runtime.Exception ex) { if (ex.ErrorStatus == Autodesk.AutoCAD.Runtime.ErrorStatus.InvalidInput) { MyCustomObject myCustomObj = new MyCustomObject(); myCustomObj.Vertices = new List<Point3d>(); for (int i = 0; i < acNewPLine2.NumberOfVertices; i++) { myCustomObj.Vertices.Add(acNewPLine2.GetPoint3dAt(i)); } myCustomObjList.Add(myCustomObj); } else { // Throw out other exceptions throw ex; } } finally { acNewPLine.Dispose(); if (acDboCol_2 != null) acDboCol_2.Dispose(); } } } finally { if (acDboCol_1 != null) acDboCol_1.Dispose(); } Quote
SEANT Posted September 7, 2010 Posted September 7, 2010 Everything looks good. What is going on in “MyCustomObject”? Is it possible that a problem resides in the MyCustomObject to Database.Polyline process? One thing to consider; there are two types of polylines -that can appear the same - in the drawing editor. One can have four vertices (for example) while another has five, based on how and when a polyline is closed. See attached. PolyExample.dwg Quote
stmoong Posted September 7, 2010 Author Posted September 7, 2010 “MyCustomObject” is just a business object that holds a list of vertices at the moment, using a generic List. Regarding the "PolyExample.dwg", I don't see any difference, is there a command to check number of vertices? (besides writing code and displaying them ) I tested the sorting code. It works for my current set of test data. Might need to do that on the first split depending on the order of the vertices passed in. I notice every time we split, one of the split polylines will have the "bad" order. List<MyCustomObject> myCustomObjList = new List<MyCustomObject>(); DBObjectCollection acDboCol_1 = null; try { acDboCol_1 = acPoly.GetSplitCurves(acPt3dCol_1); foreach (Entity acEnt in acDboCol_1) { Polyline acNewPLine = acEnt as Polyline; acNewPLine.Closed = true; // Sort the polyline so that the starting point for the second split-line lies on the first segment of the polyline. // This is required because the splitting will give strange result when attempt to split a polyline where the first segment does not // contain the start point of the split line... for (int i = 0; i < acNewPLine.NumberOfVertices; i++) { LineSegment3d acTmpLine = acNewPLine.GetLineSegmentAt(i); if (acTmpLine.IsOn(acPt3dCol2Start)) { // Sort if it's not on the first segment if (acNewPLine.StartPoint != acTmpLine.StartPoint) { List<Point3d> acTmpPts = new List<Point3d>(); for (int j = i; j < acNewPLine.NumberOfVertices; j++) { acTmpPts.Add(acNewPLine.GetPoint3dAt(j)); } for (int j = 0; j < i; j++) { acTmpPts.Add(acNewPLine.GetPoint3dAt(j)); } acNewPLine.Reset(false, 0); // clear all the existing vertices foreach (Point3d acTmpPt in acTmpPts) { // Add to the last position acNewPLine.AddVertexAt(acNewPLine.NumberOfVertices, new Point2d(acTmpPt.X, acTmpPt.Y), 0.0, 0.0, 0.0); } acNewPLine.Closed = true; } break; } } DBObjectCollection acDboCol_2 = null; try { acDboCol_2 = acNewPLine.GetSplitCurves(acPt3dCol_2); foreach (Entity acEnt2 in acDboCol_2) { Polyline acNewPLine2 = acEnt2 as Polyline; acNewPLine2.Closed = true; MyCustomObject myCustomObj = new MyCustomObject(); myCustomObj.Vertices = new List<Point3d>(); for (int i = 0; i < acNewPLine2.NumberOfVertices; i++) { myCustomObj.Vertices.Add(acNewPLine2.GetPoint3dAt(i)); } myCustomObjList.Add(myCustomObj); acNewPLine2.Dispose(); } } catch (Autodesk.AutoCAD.Runtime.Exception ex) { if (ex.ErrorStatus == Autodesk.AutoCAD.Runtime.ErrorStatus.InvalidInput) { MyCustomObject myCustomObj = new MyCustomObject(); myCustomObj.Vertices = new List<Point3d>(); for (int i = 0; i < acNewPLine2.NumberOfVertices; i++) { myCustomObj.Vertices.Add(acNewPLine2.GetPoint3dAt(i)); } myCustomObjList.Add(myCustomObj); } else { // Throw out other exceptions throw ex; } } finally { acNewPLine.Dispose(); if (acDboCol_2 != null) acDboCol_2.Dispose(); } } } finally { if (acDboCol_1 != null) acDboCol_1.Dispose(); } Quote
SEANT Posted September 7, 2010 Posted September 7, 2010 Regarding the "PolyExample.dwg", I don't see any difference, is there a command to check number of vertices? (besides writing code and displaying them ) This is what the LIST command shows. I only bring that up in case processing was base on a notion that the first and last vertices were always different. I’ll have to look at the code snippet above later today. Command: list Select objects: 1 found Select objects: 1 found, 2 total Select objects: LWPOLYLINE Layer: "0" Space: Model space Handle = 173 Closed Constant width 0.0000 area 50.0000 perimeter 30.0000 at point X= 0.0000 Y= 0.0000 Z= 0.0000 at point X= 10.0000 Y= 0.0000 Z= 0.0000 at point X= 10.0000 Y= 5.0000 Z= 0.0000 at point X= 0.0000 Y= 5.0000 Z= 0.0000 LWPOLYLINE Layer: "0" Space: Model space Handle = 175 Closed Constant width 0.0000 area 50.0000 perimeter 30.0000 at point X= 15.0000 Y= 0.0000 Z= 0.0000 at point X= 25.0000 Y= 0.0000 Z= 0.0000 at point X= 25.0000 Y= 5.0000 Z= 0.0000 at point X= 15.0000 Y= 5.0000 Z= 0.0000 at point X= 15.0000 Y= 0.0000 Z= 0.0000 Quote
SEANT Posted September 8, 2010 Posted September 8, 2010 Ah yes, I see it now. That does appear a bit suspect. For the most part I’m pleased with the performance of the API, but I do occasionally run across these anomalies. I’ll look at the similar call in the Geometry Namespace to see if it suffers the same quirk. Quote
stmoong Posted September 8, 2010 Author Posted September 8, 2010 Thanks, I'm not sure how to use the Curve2d.GetSplitCurves found in Geometry namespace because it is expecting a double value as the input parameter. Quote
SEANT Posted September 10, 2010 Posted September 10, 2010 Here is one example of using the Geometry namespace to split a Polyline (Limited testing). I’m not sure the splitting process lends itself to a simple algorithm; the one attached uses a fairly complex looping structure similar to our originals. Certainly, though, a simpler structure may be available. I didn’t use the Curve2d.GetSplitCurves method as it only allows one parameter value input, and the process lends itself to using two intersections concurrently. Thanks, I'm not sure how to use the Curve2d.GetSplitCurves found in Geometry namespace because it is expecting a double value as the input parameter. A curves parameter at a specific intersection could be acquired via CurveCurveIntersector2d. GetIntersectionParameters(). A integer specifying which intersection to use is passed in and an Double[2] is returned. Double [0] is the parameter on Curve1, Double [1] for Curve2. [CommandMethod("SlP")] static public void SlcPly() { Database db = HostApplicationServices.WorkingDatabase; Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor; using (Transaction trans = db.TransactionManager.StartTransaction()) { PromptEntityOptions peo = new PromptEntityOptions("\nSelect a closed LWpolyline: "); peo.SetRejectMessage("\nOnly closed PolyLines! "); peo.AddAllowedClass(typeof(Polyline), true); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; Polyline pl = trans.GetObject(per.ObjectId, OpenMode.ForRead) as Polyline; if (!pl.Closed || pl.HasBulges) { ed.WriteMessage("\nPolyline is not closed or has bulges - Operation cancelled!"); return; } pl.Highlight(); Curve2dCollection Interim = new Curve2dCollection(); CompositeCurve2d original = CompCrvFromPts(ptsFromPoly(pl.ObjectId)); peo.RemoveAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Line), false); peo.SetRejectMessage("\nOnly a Line! "); peo.Message = "\nSelect a slicing line: "; per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; Point2dCollection p2dColl = new Point2dCollection(2); Line ln = trans.GetObject(per.ObjectId, OpenMode.ForRead, false) as Line; p2dColl.Add( new Point2d(ln.StartPoint.X, ln.StartPoint.Y)); p2dColl.Add(new Point2d(ln.EndPoint.X, ln.EndPoint.Y)); Interim = SliceCompCurve(original, p2dColl); p2dColl.Clear(); per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; pl.Unhighlight(); ln = trans.GetObject(per.ObjectId, OpenMode.ForRead, false) as Line; p2dColl.Add(new Point2d(ln.StartPoint.X, ln.StartPoint.Y)); p2dColl.Add(new Point2d(ln.EndPoint.X, ln.EndPoint.Y)); BlockTableRecord currSpace = trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; ObjectIdCollection oids = new ObjectIdCollection(); foreach (Curve2d c2d in Interim) { Curve2dCollection temp = SliceCompCurve(c2d as CompositeCurve2d, p2dColl); if (temp != null) { foreach (Curve2d c2d2 in temp) { oids.Add(PolyFromCompCrv(c2d2, ref db, ref currSpace)); } } else { oids.Add(PolyFromCompCrv(c2d, ref db, ref currSpace)); } } pl.UpgradeOpen(); pl.Erase(); trans.Commit(); } } static Curve2dCollection SliceCompCurve(CompositeCurve2d cc2d, Point2dCollection p2dColl) { Line2d splitter; Curve2dCollection CurvesToSend = new Curve2dCollection(); splitter = new Line2d(p2dColl[0], p2dColl[1]); CurveCurveIntersector2d ccInt = new CurveCurveIntersector2d(cc2d, splitter); int intCount = ccInt.NumberOfIntersectionPoints; if (intCount == 0) { return null; } else { Point2dCollection right = new Point2dCollection(); Point2dCollection left = new Point2dCollection(); Curve2d[] arrCurves = cc2d.GetCurves(); int CompCount = arrCurves.GetUpperBound(0); Point2d first = ccInt.GetPointOnCurve1(0).Point; Point2d second = ccInt.GetPointOnCurve1(1).Point; CompositeParameter[] compParams = new CompositeParameter[2]; compParams[0] = cc2d.GlobalToLocalParameter(ccInt.GetIntersectionParameters(0)[0]); compParams[1] = cc2d.GlobalToLocalParameter(ccInt.GetIntersectionParameters(1)[0]); for (int j = 0; j <= CompCount; j++)//Add the appropriate points to each side of the split { if (j <= compParams[0].SegmentIndex) { left.Add(arrCurves[j].StartPoint); continue; } if (j > compParams[0].SegmentIndex && j <= compParams[1].SegmentIndex) { if (j == compParams[0].SegmentIndex + 1) { left.Add(first); right.Add(first); } right.Add(arrCurves[j].StartPoint); if (j == compParams[1].SegmentIndex) { right.Add(second); left.Add(second); } } if (j > compParams[1].SegmentIndex) { left.Add(arrCurves[j].StartPoint); } } CurvesToSend.Add(CompCrvFromPts(left)); CurvesToSend.Add(CompCrvFromPts(right)); return CurvesToSend; } } static CompositeCurve2d CompCrvFromPts(Point2dCollection p2dc) { int count = p2dc.Count; LineSegment2d[] lines = new LineSegment2d[count]; for (int i = 0; i < count; i++) { Point2d view = p2dc[i]; lines[i] = new LineSegment2d(p2dc[i], p2dc[(i + 1) % count]); } return new CompositeCurve2d(lines); } static Point2dCollection ptsFromPoly(ObjectId oid) { Point2dCollection temp = new Point2dCollection(); Database db = HostApplicationServices.WorkingDatabase; using (Transaction trans = db.TransactionManager.StartTransaction()) { Polyline p2d = trans.GetObject(oid, OpenMode.ForRead) as Polyline; int vCount = Convert.ToInt32(p2d.EndParam)-1; for (int i = 0; i < vCount; i++) temp.Add(p2d.GetPoint2dAt(i)); Point2d last = p2d.GetPoint2dAt(vCount); if (!temp.Contains(last)) temp.Add(last); } return temp; } static ObjectId PolyFromCompCrv(Curve2d c2d, ref Database db, ref BlockTableRecord currSpace) { using (Transaction trans = db.TransactionManager.StartTransaction())//Nested transaction { CompositeCurve2d compCrv = c2d as CompositeCurve2d; Curve2d[] curves = (compCrv).GetCurves(); Polyline newpl = new Polyline(); try { int count = curves.GetUpperBound(0); newpl.AddVertexAt(0, curves[0].StartPoint, 0, 0, 0); for (int j = 0; j < count; j++) newpl.AddVertexAt(j + 1, curves[j].EndPoint, 0, 0, 0); newpl.AddVertexAt(count + 1, curves[count].EndPoint, 0, 0, 0); newpl.Closed = true; currSpace.AppendEntity(newpl); newpl.SetDatabaseDefaults(); trans.AddNewlyCreatedDBObject(newpl, true); return newpl.ObjectId; } catch { return ObjectId.Null; } finally { trans.Commit(); } } } Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.