Author Topic: polyline area  (Read 7565 times)

0 Members and 1 Guest are viewing this topic.

nekitip

  • Guest
polyline area
« on: December 19, 2014, 08:39:01 AM »
I wander if someone has some GetArea function for polylines?
Or at least idea?

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #1 on: December 19, 2014, 09:05:39 AM »
Polylines are derived from the Curve object which has the Area property. What are you wanting your GetArea() to do different than the Area property?

nekitip

  • Guest
Re: polyline area
« Reply #2 on: December 19, 2014, 09:12:01 AM »
Because Curve.Area in .NET is not showing correct values.
You can check yourself.
Values are correct only for simple polylines.

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #3 on: December 19, 2014, 09:42:56 AM »
Can you give an example?

nekitip

  • Guest
Re: polyline area
« Reply #4 on: December 19, 2014, 10:11:05 AM »
try to draw number 8 using single polyline
and try this simple code
Code - vb.net: [Select]
  1. Imports System
  2. Imports Autodesk.AutoCAD.Runtime
  3. Imports Autodesk.AutoCAD.ApplicationServices
  4. Imports Autodesk.AutoCAD.DatabaseServices
  5. Imports Autodesk.AutoCAD.Geometry
  6. Imports Autodesk.AutoCAD.EditorInput
  7.  
  8. Imports System.ComponentModel
  9. Imports System.Collections.Specialized
  10. Imports System.Collections.ObjectModel
  11. Imports System.Linq
  12.  
  13. <Assembly: CommandClass(GetType(tmpdel.MyCommands))>
  14. <Assembly: ExtensionApplication(GetType(tmpdel.MyPlugin))>
  15.  
  16.  
  17. Namespace tmpdel
  18.     Public Class MyPlugin
  19.         Implements IExtensionApplication
  20.         Public Sub Initialize() Implements IExtensionApplication.Initialize
  21.         End Sub
  22.         Public Sub Terminate() Implements IExtensionApplication.Terminate
  23.         End Sub
  24.     End Class
  25.  
  26.     Public Class MyCommands
  27.      
  28.         <CommandMethod("mytest")> _
  29.         Public Sub MyCommand()
  30.             showA(pickPoly)
  31.         End Sub
  32.  
  33.         Public Sub showA(oid As ObjectId)
  34.             Dim area As Double
  35.             Dim db As Database = HostApplicationServices.WorkingDatabase()
  36.             Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.GetDocument(db)
  37.             Using trans As OpenCloseTransaction = db.TransactionManager.StartOpenCloseTransaction()
  38.                 Dim pol As Polyline = trans.GetObject(oid, OpenMode.ForRead)
  39.                 area = pol.Area
  40.                 trans.Commit()
  41.             End Using
  42.             MsgBox(area.ToString)
  43.         End Sub
  44.  
  45.         Public Function pickPoly() As ObjectId
  46.             Dim db As Database = HostApplicationServices.WorkingDatabase()
  47.             Dim doc As Document = Application.DocumentManager.GetDocument(db)
  48.             Dim ed As Editor = doc.Editor
  49.             Dim peo As PromptEntityOptions = New PromptEntityOptions("\nSelect a polyline: ")
  50.             peo.SetRejectMessage("Selected object is not a polyline.")
  51.             peo.AddAllowedClass(GetType(Autodesk.AutoCAD.DatabaseServices.Polyline), True)
  52.             Dim per As PromptEntityResult = ed.GetEntity(peo)
  53.             If per.Status = PromptStatus.OK Then
  54.                 Return per.ObjectId
  55.             End If
  56.         End Function
  57.     End Class
  58. End Namespace

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #5 on: December 19, 2014, 10:34:11 AM »
OK, I do see that the Area property is incorrect for self intersecting polylines. Off to see what I can come up with.

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #6 on: December 19, 2014, 11:10:59 AM »
Using the Area property from the COM object returns the correct value. This extension method (in c#, sorry, not a VB person) works and does not require the COM libraries to be referenced (i.e. I used late binding)
Code - C#: [Select]
  1.         public static double GetArea(this Polyline pline)
  2.         {
  3.             object pl = pline.AcadObject;
  4.             return (double)pl.GetType().InvokeMember("Area", System.Reflection.BindingFlags.GetProperty, null, pl, null);
  5.         }
  6.  

nekitip

  • Guest
Re: polyline area
« Reply #7 on: December 19, 2014, 11:46:29 AM »
well this is insane
Yes Jeff, I've done quick test and your solution works, I think! Thank you!!!
I spent afternoon trying different hacks with using offsets and finding inner loops... because of this. So you are a savior!

Just how they get away with this things?

P.S.
Since I never use COM inside .NET, is there anything i should know about this specific implementation. Like disposing or so?
I assume that everything is fine, since pline is here received from transaction, so transaction will take care but...

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #8 on: December 19, 2014, 12:07:10 PM »
I've never done anything special with the ACAD object. COM pretty much takes care of itself.

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: polyline area
« Reply #9 on: December 19, 2014, 01:15:33 PM »
Shorthand version

Code - C#: [Select]
  1.         public static double GetArea(this Polyline pline)
  2.         {
  3.             dynamic pl = pline.AcadObject;
  4.             return (double)pl.Area;
  5.         }
Revit 2019, AMEP 2019 64bit Win 10

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #10 on: December 19, 2014, 04:27:46 PM »
Man, I still work with older versions enough that I never remember about that dynamic object...almost takes the fun out of figuring this stuff out :-)

nekitip

  • Guest
Re: polyline area
« Reply #11 on: December 19, 2014, 05:39:25 PM »
i was searching trough various functions in ac and found this
Code - vb.net: [Select]
  1. Dim curve As Curve = trans.GetObject(oid, OpenMode.ForRead)
  2. Dim cge As Curve3d = curve.GetGeCurve()
  3. Dim ci As New CurveCurveIntersector3d(cge, cge, curve.GetPlane.Normal)
  4. Dim iii As Integer = ci.NumberOfIntersectionPoints()
  5. Dim area= cge.GetArea(curve.StartParam, curve.EndParam)

now, it seems to count intersections right, but area is wrong here. But I've never seen this function before, maybe I'm using it wrong. If anyone has idea?
I have also found that COM solution does have some problems when used with overrule... nothing that cant be hangled though, but... how about this curve3d way?

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: polyline area
« Reply #12 on: December 19, 2014, 05:58:10 PM »

This extension method (in c#, sorry, not a VB person) works and < ... >


That's one of the 5 things you never need to apologise for Jeff .

kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

nekitip

  • Guest
Re: polyline area
« Reply #13 on: December 20, 2014, 04:30:51 AM »
Since COM is a bit slower, how about this way to get area out of curves?
Code - vb.net: [Select]
  1. Imports System
  2. Imports Autodesk.AutoCAD.Runtime
  3. Imports Autodesk.AutoCAD.ApplicationServices
  4. Imports Autodesk.AutoCAD.DatabaseServices
  5. Imports Autodesk.AutoCAD.Geometry
  6. Imports Autodesk.AutoCAD.EditorInput
  7.  
  8. Imports System.ComponentModel
  9. Imports System.Collections.Specialized
  10. Imports System.Collections.ObjectModel
  11. Imports System.Linq
  12.  
  13. <Assembly: CommandClass(GetType(tmpdel.MyCommands))>
  14. <Assembly: ExtensionApplication(GetType(tmpdel.MyPlugin))>
  15.  
  16.  
  17. Namespace tmpdel
  18.     Public Class MyPlugin
  19.         Implements IExtensionApplication
  20.         Public Sub Initialize() Implements IExtensionApplication.Initialize
  21.         End Sub
  22.         Public Sub Terminate() Implements IExtensionApplication.Terminate
  23.         End Sub
  24.     End Class
  25.  
  26.     Public Class MyCommands
  27.  
  28.        
  29.         <CommandMethod("mytest2")> _
  30.         Public Sub MyCommand2()
  31.             showA(pickCurve) 'make sure you pick curve
  32.         End Sub
  33.  
  34.  
  35. ' this is found online test from one on developers. dont work well
  36. 'http://adndevblog.typepad.com/autocad/2012/05/how-to-detect-if-a-polyline-is-self-intersecting.html
  37.         <CommandMethod("SelfIntersectPline")> _
  38.         Public Shared Sub SelfIntersectPline()
  39.  
  40.             Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  41.             Dim db As Database = doc.Database
  42.             Dim ed As Editor = doc.Editor
  43.  
  44.             Dim peo As New PromptEntityOptions(vbLf & "Select Polyline: ")
  45.  
  46.             peo.SetRejectMessage(vbLf & "Must be a Polyline...")
  47.             peo.AddAllowedClass(GetType(Polyline), True)
  48.  
  49.             Dim per As PromptEntityResult = ed.GetEntity(peo)
  50.  
  51.             If per.Status <> PromptStatus.OK Then
  52.                 Return
  53.             End If
  54.  
  55.             Using Tx As Transaction = db.TransactionManager.StartTransaction()
  56.                 Dim polyline As Polyline = TryCast(per.ObjectId.GetObject(OpenMode.ForRead), Polyline)
  57.  
  58.                 Dim entities As New DBObjectCollection()
  59.                 polyline.Explode(entities)
  60.  
  61.                 For i As Integer = 0 To entities.Count - 1
  62.                     For j As Integer = i + 1 To entities.Count - 1
  63.                         Dim curve1 As Curve = TryCast(entities(i), Curve)
  64.                         Dim curve2 As Curve = TryCast(entities(j), Curve)
  65.  
  66.                         Dim points As New Point3dCollection()
  67.                         curve1.IntersectWith(curve2, Intersect.OnBothOperands, points, IntPtr.Zero, IntPtr.Zero)
  68.  
  69.                         For Each point As Point3d In points
  70.                             ' Make a check to skip the start/end points
  71.                             ' since they are connected vertices
  72.                             If point = curve1.StartPoint OrElse point = curve1.EndPoint Then
  73.                                 If point = curve2.StartPoint OrElse point = curve2.EndPoint Then
  74.                                     ' If two consecutive segments, then skip
  75.                                     If j = i + 1 Then
  76.                                         Continue For
  77.                                     End If
  78.                                 End If
  79.                             End If
  80.  
  81.                             ed.WriteMessage(vbLf & " - Intersection point: " + point.ToString())
  82.                         Next
  83.                     Next
  84.  
  85.                     ' Need to be disposed explicitely
  86.                     ' since entities are not DB resident
  87.                     entities(i).Dispose()
  88.                 Next
  89.             End Using
  90.         End Sub
  91. '----------------------end of developers part
  92.  
  93.  
  94.         Public Shared Function GetAreaCom(curve As Curve) As Double
  95.             Dim pl = curve.AcadObject
  96.             Return DirectCast(pl.Area, Double)
  97.         End Function        
  98.  
  99.         Public Sub showA(oid As ObjectId)
  100.             Dim area As Double
  101.             Dim db As Database = HostApplicationServices.WorkingDatabase()
  102.             Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.GetDocument(db)
  103.             Using trans As OpenCloseTransaction = db.TransactionManager.StartOpenCloseTransaction()
  104.  
  105.                 Dim curve As Curve = trans.GetObject(oid, OpenMode.ForRead)
  106.                 'polyline area shows wrong value for self intersected polyline, however COM shows right, but slower
  107.                 If curve.GetRXClass.IsDerivedFrom(RXClass.GetClass(GetType(Xline))) Then
  108.                     area = 0
  109.                 Else
  110.                     If curve.GetRXClass = RXClass.GetClass(GetType(Polyline)) Then
  111.                         Dim cge As Curve3d = curve.GetGeCurve()
  112.                         Dim ci As New CurveCurveIntersector3d(cge, cge, curve.GetPlane.Normal)
  113.                        
  114.                         If ci.NumberOfIntersectionPoints() > 0 Or Not curve.Closed Then
  115.                             MsgBox("self-intersect or not closed")
  116.                             area = GetAreaCom(curve)
  117.                         Else
  118.                             area = curve.Area
  119.                         End If
  120.                         'this would be nice, but dont work
  121.                         '_area = cge.GetArea(curve.StartParam, curve.EndParam)
  122.                         cge.Dispose()
  123.                         ci.Dispose()
  124.                     Else
  125.                         area = curve.Area
  126.                     End If
  127.                 End If
  128.                 trans.Commit()
  129.             End Using
  130.             MsgBox(area.ToString)
  131.         End Sub        
  132.  
  133.         Public Function pickCurve() As ObjectId
  134.             Dim db As Database = HostApplicationServices.WorkingDatabase()
  135.             Dim doc As Document = Application.DocumentManager.GetDocument(db)
  136.             Dim ed As Editor = doc.Editor
  137.             Dim peo As PromptEntityOptions = New PromptEntityOptions("Select a curve: ")
  138.             peo.SetRejectMessage("Selected object is not a curve.")
  139.             peo.AddAllowedClass(GetType(Autodesk.AutoCAD.DatabaseServices.Curve), False)
  140.             Dim per As PromptEntityResult = ed.GetEntity(peo)
  141.             If per.Status = PromptStatus.OK Then
  142.                 Return per.ObjectId
  143.             End If
  144.         End Function      
  145.     End Class
  146. End Namespace

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: polyline area
« Reply #14 on: December 22, 2014, 07:43:40 AM »
How much slower is using the dynamic version?
Revit 2019, AMEP 2019 64bit Win 10

nekitip

  • Guest
Re: polyline area
« Reply #15 on: December 22, 2014, 08:13:30 AM »
i can't really test for C#, since dynamic in VB is sort of native, and (if I remember) keyword doesn't exist in VB, so this code in VB.NET (calling COM)
Code - vb.net: [Select]
  1.        Public Shared Function GetAreaCom(curve As Curve) As Double
  2.            Dim pl = curve.AcadObject
  3.            Return DirectCast(pl.Area, Double)
  4.        End Function        

will run two times slower then .NET code (reading Curve.Area)

but again, COM solution has its problems in overrules so it's usefull only when reading with transaction

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #16 on: December 22, 2014, 09:31:43 AM »
A quick test shows that the dynamic/COM version is a LOT slower. Although for most uses it would be negligible.
Quote
Command: POLYAREA
Select polyline:
 .NET Area = 5288757.29 in 00:00:00.0030002, COM Area = 5288757.29 in 00:00:01.1790674. (10,000 iterations)

Then editing the GetArea() to check for the selfintersecting polylines, as nekitip suggests, is slower than the .NET version by a fraction:
Quote
.NET Area = 5288757.29 in 00:00:00.0020001, COM Area = 5288757.29 in 00:00:00.2860164. (10,000 iterations)

nekitip

  • Guest
Re: polyline area
« Reply #17 on: December 22, 2014, 10:42:21 AM »
after two days of trial and error, and exploring wast, undocumented namespaces, this may by almost finished!
here is the code that will check if .NET area is correct. If it is - use curve.area to read, if not, use COM. However, do not use COM in overrules! In that case you can't tell area.
The way to go would be to try to see if GECURVE can bring some area.

Here is the code to check if .NET API is good for read. I HAVE NOT TESTED THIS ALL TROUGH!
Code - vb.net: [Select]
  1. Public Shared Function isAreaOK(testcurve As Curve) As Boolean
  2.             'if curve intersect with itself, including imaginary start to end line, then its reporterd area is wrong
  3.             If testcurve.GetRXClass.IsDerivedFrom(xlineType) Then Return False
  4.             Dim selfintersection As Integer = 0
  5.             Dim cge As Curve3d = testcurve.GetGeCurve
  6.             Dim ci As New CurveCurveIntersector3d(cge, cge, testcurve.GetPlane.Normal)
  7.             selfintersection = ci.NumberOfIntersectionPoints
  8.             Dim overlapcnt As Integer = ci.OverlapCount
  9.             Dim startendintersections As Integer = 2
  10.             If testcurve.StartPoint <> testcurve.EndPoint Then
  11.                 Dim cconect As New Line(testcurve.StartPoint, testcurve.EndPoint)
  12.                 Dim cgconnect As Curve3d = cconect.GetGeCurve
  13.                 Dim cstartend As New CurveCurveIntersector3d(cge, cgconnect, testcurve.GetPlane.Normal)
  14.                 startendintersections = cstartend.NumberOfIntersectionPoints
  15.  
  16.                 cconect.Dispose()
  17.                 cgconnect.Dispose()
  18.                 cstartend.Dispose()
  19.  
  20.             End If
  21.  
  22.             cge.Dispose()
  23.             ci.Dispose()
  24.  
  25.             'sits on itself partialy
  26.             If overlapcnt > 0 Then Return False
  27.  
  28.             If startendintersections = 2 Or startendintersections = 0 Then
  29.                 '0 = single line, single vertice
  30.                 '2 = non self-intersecting polyline with 2 or more vertices, and imaginary line connects startpoint and endpoint
  31.                 If selfintersection = 0 Then Return True
  32.             End If
  33.             Return False
  34.  
  35.         End Function

concerns:
-disposing?
-is startpoint=endpoint comparing doubles? if so, this may also be unpredictable test
-3d?
EDIT: sorry, I had declared xline somewhere else Public Shared xlineType = RXClass.GetClass(GetType(Xline))
« Last Edit: December 22, 2014, 10:47:25 AM by nekitip »

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: polyline area
« Reply #18 on: December 22, 2014, 01:16:44 PM »
Why can we not use COM in overrules?

What about converting closed polyline to region and using its area property?
Revit 2019, AMEP 2019 64bit Win 10

Jeff_M

  • King Gator
  • Posts: 4095
  • C3D user & customizer
Re: polyline area
« Reply #19 on: December 22, 2014, 02:24:45 PM »
FYI, this is the response I got from Autodesk's DevHelp regarding the Curve.Area property:
Quote from: ADN
The reason for this difference is due to the way the curve area is computed as compared to the AREA command. A curve's area is computed by integration over the curve. In an intersecting polyline, one of the loop is clockwise and the other is counter-clockwise and due to the sign the area is the difference of the loop areas.


To compute the polyline area just as the AREA command in AutoCAD does, it is required to consider the intersection points of the polyline and compute the area of the loops separately and sum them. The value that you see in AutoCAD's property palette and the value from the COM API both use this method.


To determine if the polyline is self-intersecting, you will find this blog post helpful :

http://adndevblog.typepad.com/autocad/2012/05/how-to-detect-if-a-polyline-is-self-intersecting.html

nekitip

  • Guest
Re: polyline area
« Reply #20 on: December 22, 2014, 03:33:19 PM »
Why can we not use COM in overrules?
If you can, than I'm doing something wrong.

nekitip

  • Guest
Re: polyline area
« Reply #21 on: December 22, 2014, 03:40:06 PM »
FYI, this is the response I got from Autodesk's DevHelp regarding the Curve.Area property:
Quote from: ADN
The reason for this difference is due to the way the curve area is computed as compared to the AREA command. A curve's area is computed by integration over the curve. In an intersecting polyline, one of the loop is clockwise and the other is counter-clockwise and due to the sign the area is the difference of the loop areas.


To compute the polyline area just as the AREA command in AutoCAD does, it is required to consider the intersection points of the polyline and compute the area of the loops separately and sum them. The value that you see in AutoCAD's property palette and the value from the COM API both use this method.


To determine if the polyline is self-intersecting, you will find this blog post helpful :

http://adndevblog.typepad.com/autocad/2012/05/how-to-detect-if-a-polyline-is-self-intersecting.html
As I have said in a few posts before, both this anwser and this blog (as referenced in my earlier code) is incomplete. This is problem of AutoCAD, nothing is ever complete, and always blog blog blog... never documentation.

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: polyline area
« Reply #22 on: December 23, 2014, 08:22:10 AM »
Why can we not use COM in overrules?
If you can, than I'm doing something wrong.


I had never tried using COM inside an overrule before.  I avoid COM unless there is no other alternative. So after some experimenting you're right about ObjectOverrules. I can't get it to work no matter how I attempt to use it inside an ObjectOverrule. DrawableOverrules and GripOverrules seem to handle COM just fine though. I was able to get properties inside both WorldDraw and GetGripPoints methods.
Revit 2019, AMEP 2019 64bit Win 10

nekitip

  • Guest
Re: polyline area
« Reply #23 on: December 23, 2014, 09:04:11 AM »
I'm kind of feeling that reading COM also modifies object in some way. Like entity has some quantum state you disturb just by reading it :)
I haven't try i worlddraw since COM is slow.

Has anyone looked into my isareaOK routine? This may look like promising way to go, but I have my concens about both routine (disposing, 3d, elevations, vieports...), and also - will it work in all cases. Since, half good is no good.