PDA

View Full Version : Dividing a polyline

stmoong
16th Aug 2010, 11:16 am
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.

22412

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.

SEANT
16th Aug 2010, 12:02 pm
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.

stmoong
17th Aug 2010, 01:24 am
Hi Seant,

I'm using LW-polylines. They are aligned to the WCS. No Arcs for the polys.

stmoong
17th Aug 2010, 08:41 am
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

SEANT
17th Aug 2010, 09:48 am
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;

[CommandMethod("SlicePoly")]
static public void SlcPly()
{
Database db = HostApplicationServices.WorkingDatabase;
using (Transaction trans = db.TransactionManager.StartTransaction())
{

PromptEntityOptions peo = new PromptEntityOptions("\nSelect a closed LWpolyline: ");
peo.SetRejectMessage("\nOnly closed PolyLines! ");

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.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();
}
catch
{
ed.WriteMessage("\nError processing geometry!");
dboc.Dispose();
pl.Unhighlight();
trans.Abort();
return;
}
}
pl.Erase();
}
dboc.Dispose();
trans.Commit();
}

}

22438

stmoong
17th Aug 2010, 11:22 am
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().

SEANT
17th Aug 2010, 11:54 am
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.

stmoong
18th Aug 2010, 10:32 am
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?

SEANT
18th Aug 2010, 11:21 am
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.

stmoong
19th Aug 2010, 02:10 am
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.
22502

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

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

SEANT
20th Aug 2010, 01:04 am
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.

stmoong
20th Aug 2010, 01:30 am
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?

SEANT
20th Aug 2010, 01:55 am
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.

stmoong
20th Aug 2010, 03:17 am
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....

22531

For step 3:
No idea yet...

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

SEANT
20th Aug 2010, 10:56 am
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:

Int stVert = 1
LineSegment2d start = PolyLine.GetLineSegment2dAt(stVert)
LineSegment2d end = PolyLine.GetLineSegment2dAt((stVert + 3) % 4)

stmoong
20th Aug 2010, 11:18 am
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:

SEANT
20th Aug 2010, 11:41 am
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.