Jump to content

Dividing a polyline


Recommended Posts

Hi,

 

I'm trying to divide up a closed polyline with the blue line that intersects with it. I'm able to get the vertices for the intersection points.

 

The end result should have 2 separate closed polylines.

 

I've checked on the internet, and found some Lisp code, which unfortunately, I do not know how to interpret :P

 

Does anyone know how to go about it? I'm progamming in C# .Net. Thank you.

 

sample.jpg

 

Special cases I will need to work out later:

1) One of the intersection points are on a vertex.

2) Both intersection points are on different vertices.

3) Both intersection points are on the same axis, i.e. the blue line is on one of the edges.

Link to post
Share on other sites

A couple areas for clarification:

 

Are you dealing with 3dpolys, or strictly LW – 2Dpolys?

If strictly 2d, are they always aligned to the WCS?

Are there any Arcs(Bulges) to the polys?

 

I will probably not be around much today, but these questions may help clarify the situation for anyone offering more timely help.

Link to post
Share on other sites

Hi,

 

I managed to dig through the ObjectArx Managed Class Reference documentation and found this useful method -> Curve.GetSplitCurves(Point3dCollection).

 

Since PolyLine is a child of the Curve class (beauty of OO), I just pass in the intersection points, and I get the two instances of the PolyLine that are split from the original one.

 

Hope this is useful for anyone looking for similar solution. :D

Link to post
Share on other sites

This is a fairly basic version; seems to work well when dealing with singular lumps. If the poly in question would divide into complex lumps (see image) then this routine returns unexpected results. As a matter of fact, that scenario may be more easily handled with a brief circuit through the ACAD Region entity.

 

The routine does indeed use the Curve.GetSplitCurves method. :)

 

 
using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

 

 
       [CommandMethod("SlicePoly")]
       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)
               {
                   ed.WriteMessage("\nNo closed polyline detected - Operation cancelled!");
                   return;
               }
               pl.Highlight();
               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;


               Line ln = trans.GetObject(per.ObjectId, OpenMode.ForRead, false) as Line;
               Point3dCollection p3dc = new Point3dCollection();
               //pl.IntersectWith(ln, Intersect.OnBothOperands, p3dc, 0,0);  //For earlier versions of AutoCAD
               pl.IntersectWith(ln, Intersect.OnBothOperands, p3dc, IntPtr.Zero, IntPtr.Zero);
               DBObjectCollection dboc = pl.GetSplitCurves(p3dc);

               if (dboc.Count > 1)
               {
                   BlockTableRecord currSpace = trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                   foreach(Entity ent in dboc)
                   {
                       try
                       {
                           Polyline subPoly = ent as Polyline;
                           subPoly.Closed = true;
                           currSpace.AppendEntity(subPoly);
                           subPoly.SetDatabaseDefaults();
                           trans.AddNewlyCreatedDBObject(subPoly, true);
                       }
                       catch
                       {
                           ed.WriteMessage("\nError processing geometry!");
                           dboc.Dispose();
                           pl.Unhighlight();
                           trans.Abort();
                           return;
                       }
                   }
                   pl.UpgradeOpen();
                   pl.Erase();
               }
               dboc.Dispose();
               trans.Commit();
           } 

       } 


 

MultiLump.jpg

Link to post
Share on other sites

Thanks, Seant.

 

I modified my code to use PromptEntityResult as well. Previously, I was using PromptSelectionResult, which I then set to select one object in the PromptSelectionOptions.

 

Is the trans.Abort() necessary? If the operation failed, the transaction is not committed right?

 

The reason I'm asking is AutoCad crash when I do throw ApplicationException (for eg. when user cancel the operation or when the polyline is not closed), and in there, I call trans.Abort().

Link to post
Share on other sites

There are several issues likely with my example (one of which was my forgetting to UnHighlight the poly if there were interruptions during the line selection). That is a hazard of “whipping up” examples. :oops:

 

Trans.Commit and/or Trans.Abort may be optional (perhaps depending on the circumstance). It is quite possibly a matter of preference; the desire to code in a very explicit fashion. If there were previous Trans.AddNewlyCreatedDBObject, I feel more comfortable with Abort.

The “Through the Interface” blog does recommend the Trans.Commit (better performance than Trans.Abort) if there was no specific database transaction.

Edited by SEANT
Grammar
Link to post
Share on other sites

No worry with the example code, it's great for reference. I'm quite new to AutoCad (like 3 weeks), so I'm not sure if the way the transaction for AutoCad works the same as the one in SQL database.

 

Anyway, I need to modify the program to automatically divide up the closed polyline shape without having user draw the line and selecting the entity. The division is based on some fixed shaped quadrilaterals. I've looked around, and found some algorithms for triangulation, which don't really serve the purpose.

 

It's like tessellating a polygon with rectangles.

 

Do you have any suggestions on how to go about doing this?

Edited by stmoong
Added more info
Link to post
Share on other sites

Post a couple of before and after examples. That is usually the best way to generate interest in the many gifted programmers frequenting this forum.

Link to post
Share on other sites

I have implemented step 1.

 

For step 2, how do I draw a line that has a certain offset from the points using code? The points are denoted by circles for convenience.

 

For step 3, it's basically splitting up those polylines created in step 2, so that the dimension is within the dimension of certain specified metal plates.

 

 

Here are the steps and screenshots of drawings that I've created manually for illustration purposes.

 

1) First, the program draws closed polylines (let's call this the outer frame) using coordinates provided.

model_1.jpg

 

2) Next, the program draws a new set of closed polylines (let's call this inner frame) based on location of several points inside the outer frame.

model_2.jpg

 

3) Next, the program draws another set of closed polylines (let's call this support frame) based on the inner frame and also constrained by the size of a specified metal plates, that will evenually sit on this support frame.

model_3.jpg

 

 

Thanks in advance.

Link to post
Share on other sites

Looks pretty tricky – certainly from an outsider’s perspective.

 

Presumably there is some key geometry to which the lines added in step 2 stay either parallel or perpendicular. For instance, they don’t have any apparent relation to the shortest side of the polygon.

 

If the key geometry was the perpendicular sides then I’d be tempted to use the Autodesk.AutoCAD.Geometry Line3d Constructor

 

New Line3d(Point3d, Vector3d)

 

with a vector set up to be perpendicular to the appropriate side. That Line3d could be offset the desired amount (towards the key geometry’s midpoint) then queried where they intersect the key and axis geometry.

 

Then those intersections could be used to construct a Autodesk.AutoCAD.DatabaseServices Line(Point3d, Point3d)

 

If the key geometry were the parallel line then a similar process could be used to create Autodesk.AutoCAD.Geometry Line3d with vectors set up as parallel. The points would have to be analyzed to fin which were closest then the offsets would in or out based on distance.

Step 3 would be more of the same, based on whatever is considered the key geometry.

Link to post
Share on other sites

For part 2, I have similar idea of offsetting a line drawn from a point and perpendicular to an edge. The problem is how to determine the edge to be used as the reference. I thought about the orientation of the structure, so, if we can somehow do a sweep (clockwise or anti-clockwise) and use the first and last edge, then we have our reference edges.

 

For part 3, similarly, we need to determine the start vertex, the direction along the edge and which edge so that we can run perpendicular line along the edge.

 

If we can translate this to Autocad or trigonometry / geometry maths, then we should be able to solve for this geometry shape. Any idea?

Link to post
Share on other sites
For part 2, I have similar idea of offsetting a line drawn from a point and perpendicular to an edge. The problem is how to determine the edge to be used as the reference. I thought about the orientation of the structure, so, if we can somehow do a sweep (clockwise or anti-clockwise) and use the first and last edge, then we have our reference edges.

 

Determining the first and last segment would be easy enough, but where to start the sweep, and why?

In step 1, what is the algorithm to create the initial split? Is it based on symmetry?

Certainly a split that lands on a vertex would serve as a good start point.

Link to post
Share on other sites

For step 1:

The coordinates of vertices are provided, so the drawing is easily done. Also, I was not being clear, there are 2 closed polylines, rather than 1 polyline.

 

For step 2:

The 4 points, denoted by circles, are based off a center (this coordinate will be provided by user) somewhere on the right corner (in this particular case). So, in effect, there are 2 imaginary arcs whereby 2 points lie on the outer arc, while the other 2 points lie on the inner arc.

 

Well, it seems that this discussion gives me an idea :)

 

For the 2 points that lie on outer arc, I could draw a circle and find out the intersection points between the circle and the 2 polylines. In this case, I would get 4 intersection points. Then, I discard the 2 intersection points on the common edges in the middle since they are the same. So, I would get the 2 intersection points, from which I could compute the points on the reference edge. Repeat steps for the 2 points on inner arc. Now I have to figure out the maths formula....

 

model_4.jpg

 

For step 3:

No idea yet...

 

 

By the way, just curious, how would you determine the first and last segment?

Link to post
Share on other sites
For step 1:

The coordinates of vertices are provided, so the drawing is easily done. Also, I was not being clear, there are 2 closed polylines, rather than 1 polyline.

 

For step 2:

The 4 points, denoted by circles, are based off a center (this coordinate will be provided by user) somewhere on the right corner (in this particular case). So, in effect, there are 2 imaginary arcs whereby 2 points lie on the outer arc, while the other 2 points lie on the inner arc.

 

Well, it seems that this discussion gives me an idea :)

 

For the 2 points that lie on outer arc, I could draw a circle and find out the intersection points between the circle and the 2 polylines. In this case, I would get 4 intersection points. Then, I discard the 2 intersection points on the common edges in the middle since they are the same. So, I would get the 2 intersection points, from which I could compute the points on the reference edge. Repeat steps for the 2 points on inner arc. Now I have to figure out the maths formula....

 

 

 

Autodesk.AutoCAD.Geometry Namespace has more functionality (than does Autodesk.AutoCAD.DatabaseServices Curve/Entity) devoted to intersections.

 

If the Polyline were setup as a CompositeCurve2d, and the arcs as CircularArc2d, then those two objects could be passed into a CurveCurveIntersector2d object. The CurveCurveIntersector2d.GetPointOnCurve1 method would return a PointOnCurve2d, which could be queried to find the appropriate reference edge.

 

By the way, just curious, how would you determine the first and last segment?

 

 

 

If the number of vertices are known (presumably 4), and we knew the starting vertex (let’s say vertex #2 [or index 1 with 0 based indexing])) then the statements:

 

 
[size=2]Int stVert = 1[/size]
[size=2]LineSegment2d start = PolyLine.GetLineSegment2dAt(stVert)[/size]
[size=2]LineSegment2d end = PolyLine.GetLineSegment2dAt((stVert + 3) % 4)[/size]

Link to post
Share on other sites

So, if I understand right, I should be using the Autodesk.AutoCAD.Geometry namespace for computation and getting the various coordinates, then use Autodesk.AutoCAD.DatabaseServices namespace to creating the drawing?

 

Thank you so much for your kind assistance.

 

ps: I cracked my head working on those matrix formula :sweat:

Link to post
Share on other sites
So, if I understand right, I should be using the Autodesk.AutoCAD.Geometry namespace for computation and getting the various coordinates, then use Autodesk.AutoCAD.DatabaseServices namespace to creating the drawing?

That's more or less what I do, if the calculations warrant it.

 

 

 

Thank you so much for your kind assistance.

 

 

You're welcome.

Link to post
Share on other sites

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