Jump to content

Using the GetSplitCurves(Point3dCollection) method


Recommended Posts

Posted (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.

poly_1.jpg

 

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:

poly_2.jpg

 

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 by stmoong
Image order was wrong.
Posted

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.

Posted (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.

 

 

test_1.jpg

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).

test_2.png

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 by stmoong
Clarification on using Polyline
Posted (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! :oops:

Edited by SEANT
Buggy code deleted
Posted

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. :geek:

 

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();
           }

Posted

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

Posted

“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 :P)

 

 

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.

 

:bloodshot:

 

           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();
           }

Posted

 

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 :P)

 

 

 

 

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

Posted

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.

Posted

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.

Posted

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.:geek:

 

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();
               }
           }
       }

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...