Hi All,
I have some closed polyline in a rectangular area like below screen shot. I want numbering these polylines counter clockwise or clockwise by selecting a reference polyline. In the below screen shot , I have highlighted a polyline (red marked). So , first I select that polyline and ask to numbering other polylines in couter clockwise.
The result of my requirement should be like below screen shot. Can anybody give me hint regarding this matter how to do that? sample code in VB will be appreciated.
Thanks.
Zakir
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Solved by Hallex. Go to Solution.
Hi,
Just search for "clockwise" in this forum, this has been asked several times before.
Gaston Nunez
Hi,
It seems i misread your post, if i understand, you need to "sort" the polylines in a cw/ccw fashion, is that true, then you can try finding the angle to any point on the polyline (or to the centroid) from the "center" of the rectangle in the picture, and then sort on this angle. You need to find the angles in the [0,2pi] range, to do that you may try something like:
dim v as vector3D ' The vector from cp to p
dim cp as point3D ' Center point
dim p as point3D ' Some point on a polyline (or the centroid of the polyline)
dim theta as double 'The angle
v= cp.GetVectorTo(p)
theta= v.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis.Negate())
now, do that for each polyline, put theta in some data structure( a dictionary should be my first choice) and sort them.
Gaston Nunez
I would try coordinates of the centre points. But, you would need some rules and tolerances. Is it always the same pattern? Is there always 3 polygons on the right, 3 on the left, 5 across the top, 12 total, etc? Is there a chance the coordinates could be negative? Is the x value of the 3 on the right / left exactly the same - also the y value of the 5 at the top / bottom?
Not sure about if this helps, here is just a part of code
from working project of mine:
Public Function Rtd(ByVal radian As Double) As Double Return radian * (180 / Math.PI) End Function <CommandMethod("PolarInc")> _ Public Sub TestLablelPolar() Try Dim stack As Dictionary(Of ObjectId, Point3d) = New Dictionary(Of ObjectId, Point3d) Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = doc.Editor Dim ucs As CoordinateSystem3d = ed.CurrentUserCoordinateSystem.CoordinateSystem3d 'Dim plan As New Plane(Point3d.Origin, ucs.Zaxis) Dim ppo As New PromptPointOptions(vbLf & "Lower left corner: ") Dim ppr As PromptPointResult = ed.GetPoint(ppo) If ppr.Status <> PromptStatus.OK Then Return End If Dim pco As New PromptCornerOptions(vbLf & "Other corner: ", ppr.Value) Dim pcr As PromptPointResult = ed.GetCorner(pco) If pcr.Status <> PromptStatus.OK Then Return End If Dim p1 As Point3d = ppr.Value Dim p2 As Point3d = pcr.Value If p1.X = p2.X OrElse p1.Y = p2.Y Then ed.WriteMessage(vbLf & "Invalid coordinate specification") Return End If ' Calculate the middle point of the box Dim center As Point3d = p1.Add(p1.GetVectorTo(p2).DivideBy(2)) Dim filter As TypedValue() = {New TypedValue(0, "lwpolyline"), New TypedValue(70, 1)} Using tr = doc.TransactionManager.StartTransaction() '' Create a crossing window from p1 to p2 Dim setRes As PromptSelectionResult = ed.SelectCrossingWindow(p1, p2, New SelectionFilter(filter)) '' If the prompt status is OK, objects were selected If setRes.Status <> PromptStatus.OK Then Return Dim selSet As SelectionSet = setRes.Value Dim peo As New PromptEntityOptions(vbLf & "Select a lwpolyline to start (if CW order, select one before start): ") peo.SetRejectMessage(vbLf & "Not a polyline...") peo.AddAllowedClass(GetType(Polyline), True) Dim per As PromptEntityResult = ed.GetEntity(peo) If per.Status <> PromptStatus.OK Then Return End If Dim spoly As Polyline = DirectCast(tr.GetObject(per.ObjectId, OpenMode.ForRead), Polyline) Dim sext As Extents3d = spoly.GeometricExtents Dim sminp As Point3d = sext.MinPoint Dim smaxp As Point3d = sext.MaxPoint Dim scp As Point3d = New Point3d((smaxp.X + sminp.X) / 2, (smaxp.Y + sminp.Y) / 2, sminp.Z) Dim vec As Vector3d vec = center.GetVectorTo(scp) ' Dim ang As Double = Rtd(vec.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis.Negate())) For Each selObj As SelectedObject In selSet Dim ent As Entity = DirectCast(tr.GetObject(selObj.ObjectId, OpenMode.ForRead), Entity) Dim ext As Extents3d = ent.GeometricExtents Dim minp As Point3d = ext.MinPoint Dim maxp As Point3d = ext.MaxPoint Dim cp As Point3d = New Point3d((maxp.X + minp.X) / 2, (maxp.Y + minp.Y) / 2, minp.Z) stack(selObj.ObjectId) = cp Next If stack.Count = 0 Then Return Dim pko As New PromptKeywordOptions(vbLf & "Choose an order of CW or CCW " & "[CW/CCW]: ") ' Add keywords pko.Keywords.Add("CW") pko.Keywords.Add("CCW") ' The default depends on our current settings pko.Keywords.Default = "CCW" 'To get default setting pko.AllowNone = True Dim pkr As PromptResult = ed.GetKeywords(pko) Dim rev As String = pkr.StringResult If pkr.Status <> PromptStatus.OK Then Return End If Dim asc As IEnumerable(Of KeyValuePair(Of ObjectId, Point3d)) If rev.ToUpper = "CCW" Then asc = stack.OrderBy(Function(x) Rtd(center.GetVectorTo(x.Value).GetAngleTo(vec, ucs.Zaxis.Negate()))) Else asc = stack.OrderByDescending(Function(x) Rtd(center.GetVectorTo(x.Value).GetAngleTo(vec, ucs.Zaxis.Negate()))) End If ' Set increment start Dim i As Integer = 1 If asc IsNot Nothing Then Dim db As Database = doc.Database Dim btr As BlockTableRecord = DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord) For Each kvp As KeyValuePair(Of ObjectId, Point3d) In asc Dim pt As Point3d = New Point3d(kvp.Value.X, kvp.Value.Y, kvp.Value.Z) Dim txt As DBText = New DBText() txt.SetDatabaseDefaults() txt.TextString = i.ToString() txt.Height = 100 txt.TextStyleId = db.Textstyle txt.HorizontalMode = TextHorizontalMode.TextCenter txt.VerticalMode = TextVerticalMode.TextVerticalMid txt.Position = pt txt.AlignmentPoint = pt txt.AdjustAlignment(db) btr.AppendEntity(txt) tr.AddNewlyCreatedDBObject(txt, True) i += 1 Next End If tr.Commit() End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(ex.Message + vbLf + ex.StackTrace) Finally End Try End Sub
Hi,
Here's an example using Linq extension methods.
Assuming polylines are rectangular, the barycenter is used to calculate polylines centers.
Assuming polylines are organized in a rectangular area, the center point is calculated using the extents ot all polylines.
If a polyline center is on the center point, it is sorted as the last item (as the 13 polyline in your example).
C# code
[CommandMethod("Test", CommandFlags.Modal)] public void Test() { Document doc = AcAp.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; TypedValue[] filter = { new TypedValue(0, "LWPOLYLINE") }; PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter)); if (psr.Status != PromptStatus.OK) return; HashSet<ObjectId> ids = new HashSet<ObjectId>(psr.Value.GetObjectIds()); PromptEntityOptions peo = new PromptEntityOptions("\nSelect the start polyline: "); peo.SetRejectMessage("Only a polyline."); peo.AddAllowedClass(typeof(Polyline), true); PromptEntityResult per; while (true) { per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; if (ids.Contains(per.ObjectId)) break; ed.WriteMessage("\nThe start polyline must belong to the previous selection."); } ObjectId startId = per.ObjectId; PromptResult pr = ed.GetKeywords("\nChoose an option: ", "CW", "CCW"); if (pr.Status != PromptStatus.OK) return; bool clockwise = pr.StringResult == "CW"; Vector3d normal = clockwise ? Vector3d.ZAxis.Negate() : Vector3d.ZAxis; using (Transaction tr = db.TransactionManager.StartTransaction()) { var pts = psr.Value.GetObjectIds() .ToDictionary(id => id, id => GetBarycenter(id)); Point3d center = GetExtentsCenter(pts.Values); Vector3d org = center.GetVectorTo(pts[startId]); int i = 1; BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); foreach (Point3d pt in pts.Values.OrderBy(p => p.IsEqualTo(center) ? double.MaxValue : org.GetAngleTo(p - center, normal))) { DBText text = new DBText(); text.Justify = AttachmentPoint.MiddleCenter; text.AlignmentPoint = pt; text.TextString = i.ToString(); btr.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); i++; } tr.Commit(); } } private Point3d GetBarycenter(ObjectId id) { Polyline pline = (Polyline)id.Database.TransactionManager.GetObject(id, OpenMode.ForRead); Vector3d vector = new Vector3d(); int i = 0; for (; i < pline.NumberOfVertices; i++) vector += pline.GetPoint3dAt(i).GetAsVector(); return Point3d.Origin + vector / i; } private Point3d GetExtentsCenter(IEnumerable<Point3d> pts) { Extents3d ext = pts.Aggregate(new Extents3d(), (e, p) => { e.AddPoint(p); return e; }); return ext.MinPoint + ext.MinPoint.GetVectorTo(ext.MaxPoint) / 2.0; }
VB conversion
<CommandMethod("Test", CommandFlags.Modal)> _ Public Sub Test() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Dim filter As TypedValue() = {New TypedValue(0, "LWPOLYLINE")} Dim psr As PromptSelectionResult = ed.GetSelection(New SelectionFilter(filter)) If psr.Status <> PromptStatus.OK Then Return End If Dim ids As New HashSet(Of ObjectId)(psr.Value.GetObjectIds()) Dim peo As New PromptEntityOptions(vbLf & "Select the start polyline: ") peo.SetRejectMessage("Only a polyline.") peo.AddAllowedClass(GetType(Polyline), True) Dim per As PromptEntityResult = ed.GetEntity(peo) While True If per.Status <> PromptStatus.OK Then Return End If If ids.Contains(per.ObjectId) Then Exit While End If ed.WriteMessage(vbLf & "The start polyline must belong to the previous selection.") per = ed.GetEntity(peo) End While Dim startId As ObjectId = per.ObjectId Dim pr As PromptResult = ed.GetKeywords(vbLf & "Choose an option: ", "CW", "CCW") If pr.Status <> PromptStatus.OK Then Return End If Dim clockwise As Boolean = pr.StringResult = "CW" Dim normal As Vector3d = If(clockwise, Vector3d.ZAxis.Negate(), Vector3d.ZAxis) Using tr As Transaction = db.TransactionManager.StartTransaction() Dim pts = psr.Value.GetObjectIds() _ .ToDictionary(Function(id) id, Function(id) GetBarycenter(id)) Dim center As Point3d = GetExtentsCenter(pts.Values) Dim org As Vector3d = pts(startId) - center Dim i As Integer = 1 Dim btr As BlockTableRecord = _ DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord) For Each pt As Point3d In pts.Values _ .OrderBy(Function(p) _ If(p.IsEqualTo(center), Double.MaxValue, org.GetAngleTo(p - center, normal))) Dim text As New DBText() text.Justify = AttachmentPoint.MiddleCenter text.AlignmentPoint = pt text.TextString = i.ToString() btr.AppendEntity(text) tr.AddNewlyCreatedDBObject(text, True) i += 1 Next tr.Commit() End Using End Sub Private Function GetBarycenter(id As ObjectId) As Point3d Dim pline As Polyline = _ DirectCast(id.Database.TransactionManager.GetObject(id, OpenMode.ForRead), Polyline) Dim vector As New Vector3d() Dim i As Integer = 0 While i < pline.NumberOfVertices vector += pline.GetPoint3dAt(i).GetAsVector() i += 1 End While Return Point3d.Origin + vector / i End Function Private Function GetExtentsCenter(pts As IEnumerable(Of Point3d)) As Point3d Dim extents As Extents3d = _ pts.Aggregate(New Extents3d(), Function(ext, pt) ext.AddPoint(pt) Return ext End Function) Return extents.MinPoint + extents.MinPoint.GetVectorTo(extents.MaxPoint) / 2.0 End Function