Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: vermesser от 27-01-2018, 17:51:25

Название: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 17:51:25
Здравствуйте!

Подскажите, пожалуйста, существует ли какой-нибудь встроенный метод AutoCad .Net API для определения местоположения точки относительно замкнутого контура (кривой) полностью лежащий в одной плоскости: в контуре, вне контура, на контуре?

(https://s18.postimg.org/xirsk6h39/2018-01-27_17-47-05.png) (https://postimg.org/image/xirsk6h39/)

Заранее спасибо!
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: Александр Ривилис от 27-01-2018, 17:53:46
Как средствами AutoCAD определить расположение точки относительно контура. (http://adn-cis.org/kak-sredstvami-opredelit-raspolozhenie-tochki-otnositelno-kontura.html)
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 18:06:28
Спасибо! Попробую оба способа и отпишусь.
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 21:50:12
Код - vb.net [Выбрать]
  1. Imports Autodesk.AutoCAD.ApplicationServices
  2. Imports Autodesk.AutoCAD.BoundaryRepresentation
  3. Imports Autodesk.AutoCAD.DatabaseServices
  4. Imports Autodesk.AutoCAD.EditorInput
  5. Imports Autodesk.AutoCAD.Geometry
  6. Imports Autodesk.AutoCAD.Runtime
  7.  
  8. Public Class PointLocationTest
  9.  
  10.     Private doc As Document
  11.     Private ed As Editor
  12.     Private db As Database
  13.  
  14.     <CommandMethod("POINTLOCATIONTEST")>
  15.     Public Sub PointLocationTest()
  16.         doc = Core.Application.DocumentManager.MdiActiveDocument
  17.         ed = doc.Editor
  18.         db = doc.Database
  19.         Try
  20.             Using docLock As DocumentLock = doc.LockDocument
  21.                 Dim crv As Curve = Nothing  'Круг или эллипс, как кривая
  22.                 Dim pt As Point3d           'Точка, для которой определяем местоположение
  23.                 Dim entOpt As New PromptEntityOptions("")
  24.                 With entOpt
  25.                     .AllowNone = True
  26.                     .AllowObjectOnLockedLayer = True
  27.                     .Message = vbCrLf & "Укажите круг или эллипс: "
  28.                     .SetRejectMessage("Выбранный объект недопустим в качестве проектного. ")
  29.                     .AddAllowedClass(GetType(Circle), False)
  30.                     .AddAllowedClass(GetType(Ellipse), False)
  31.                 End With
  32.                 Dim entRes As PromptEntityResult
  33.                 entRes = ed.GetEntity(entOpt)
  34.                 Select Case entRes.Status
  35.                     Case PromptStatus.Cancel
  36.                         ed.WriteMessage(vbCrLf & "Операция прервана пользователем. ")
  37.                         Exit Sub
  38.                     Case PromptStatus.OK
  39.                         Using tr As Transaction = db.TransactionManager.StartTransaction
  40.                             crv = entRes.ObjectId.GetObject(OpenMode.ForRead)
  41.                             tr.Commit()
  42.                         End Using
  43.                 End Select
  44.                 Dim ptOpt As New PromptPointOptions("")
  45.                 With ptOpt
  46.                     .Message = vbCrLf & "Укажите точку: "
  47.                     .AllowNone = False
  48.                 End With
  49.                 Dim ptRes As PromptPointResult = ed.GetPoint(ptOpt)
  50.                 Select Case ptRes.Status
  51.                     Case PromptStatus.Cancel
  52.                         Exit Sub
  53.                     Case PromptStatus.OK
  54.                         pt = ptRes.Value
  55.                 End Select
  56.                 'Тестируем BrepEntity.GetPointContainment
  57.                 Dim dboc As New DBObjectCollection From {crv}
  58.                 Dim rgn As New Region
  59.                 rgn = Region.CreateFromCurves(dboc).Item(0)
  60.                 Using bRep As Brep _
  61.                     = New Brep(rgn)
  62.                     Dim ptCont As PointContainment
  63.                     bRep.GetPointContainment(pt, ptCont)
  64.                     Select Case ptCont
  65.                         Case PointContainment.Inside
  66.                             ed.WriteMessage(vbCrLf & "Brep: Внутри контура ")
  67.                         Case PointContainment.OnBoundary
  68.                             ed.WriteMessage(vbCrLf & "Brep: На контуре ")
  69.                         Case PointContainment.Outside
  70.                             ed.WriteMessage(vbCrLf & "Brep: Вне контура ")
  71.                     End Select
  72.                 End Using
  73.                 'Тестируем MPolygon
  74.                 Dim ids As New ObjectIdCollection From {crv.ObjectId}
  75.                 Using mp As MPolygon = New MPolygon
  76.                     mp.CreateLoopsFromBoundaries(ids, True, Tolerance.Global.EqualPoint)
  77.                     If mp.IsPointOnLoopBoundary(pt, 0, Tolerance.Global.EqualPoint) Then
  78.                         ed.WriteMessage(vbCrLf & "MPolygon: На контуре ")
  79.                     ElseIf mp.IsPointInsideMPolygon(pt, Tolerance.Global.EqualPoint).Count > 0 Then
  80.                         ed.WriteMessage(vbCrLf & "MPolygon: Внутри контура ")
  81.                     Else
  82.                         ed.WriteMessage(vbCrLf & "MPolygon: Вне контура ")
  83.                     End If
  84.                 End Using
  85.             End Using
  86.         Catch acadEx As Autodesk.AutoCAD.Runtime.Exception
  87.             ed.WriteMessage(vbCrLf & "Во время выполнения произошла ошибка. " & vbCrLf & acadEx.ToString)
  88.         Catch sysEx As System.Exception
  89.             ed.WriteMessage(vbCrLf & "Во время выполнения произошла ошибка. " & vbCrLf & sysEx.ToString)
  90.         End Try
  91.     End Sub
  92.  
  93. End Class
  94.  

Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: Александр Ривилис от 27-01-2018, 21:58:37
MPolygon вызывает ошибку eInvalidInput, если в качестве контура используем эллипс.
Так и должно быть. Этот метод работает только с полилиниями. Но эллипс можно апроксимировать и передать массив точек, а не сам примитив.
BREP не советую. У него невысокая точность.
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 22:01:20
Но эллипс можно апроксимировать и передать массив точек, а не сам примитив.
Подскажите, как?
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 22:02:11
Этот метод работает только с полилиниями
С кругом ещё работает.
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: Александр Ривилис от 27-01-2018, 22:02:48
Вообще-то для круга и эллипса можно использовать и другие алгоритмы. Например, для круга расстояние от центра до точки не больше радиуса - точка внутри...
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: Александр Ривилис от 27-01-2018, 22:05:03
Этот метод работает только с полилиниями
С кругом ещё работает.
Да. Еще кажется 2d-полилиния
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 22:06:46
Вообще-то для круга и эллипса можно использовать и другие алгоритмы. Например, для круга расстояние от центра до точки не больше радиуса - точка внутри...
Можно, но у меня может в итоге быть и любая несамопересекающаяся кривая.
Такая, например:

(https://s18.postimg.org/64qg0z51x/2018-01-27_22-08-07.png) (https://postimg.org/image/64qg0z51x/)

Поэтому я искал встроенный метод, работающий с замкнутыми кривыми.
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 27-01-2018, 22:37:24
BREP не советую. У него невысокая точность.
Возможно для моих нужд хватит. Но почему то этот метод определяет точку, находящуюся внутри контура, как находящуюся на контуре.
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: Александр Ривилис от 27-01-2018, 23:57:59
Но почему то этот метод определяет точку, находящуюся внутри контура, как находящуюся на контуре.
Вполне возможно, что это баг в конкретной версии AutoCAD. Рекомендую остановится на методах использующих MPolygon. После AutoCAD 2007 вроде ошибок в нём не было. Любую кривую (Curve) можно преобразовать в Curve3d при помощи метода Curve.GetGeCurve(), затем при помощи метода Curve3d.GetSamplePoints() получить массив точек, создать по ним временную Polyline и использовать для MPolygon.
 
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 28-01-2018, 00:23:07
Спасибо!
Пока ждал ответа решил делать так:
Проверять, принадлежит ли точка кривой,
и если нет, то проверять внутри или снаружи контура точка,
следующим методом: https://cedeela.fr/inside-a-curve.html#id1 (https://cedeela.fr/inside-a-curve.html#id1),
создавая луч из точки и проверяя количество точек пересечения луча с контуром.
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: Александр Ривилис от 28-01-2018, 00:39:59
Спасибо!
Пока ждал ответа решил делать так:
Проверять, принадлежит ли точка кривой,
и если нет, то проверять внутри или снаружи контура точка,
следующим методом: https://cedeela.fr/inside-a-curve.html#id1 (https://cedeela.fr/inside-a-curve.html#id1),
создавая луч из точки и проверяя количество точек пересечения луча с контуром.
Тоже возможный вариант, но нужно учесть граничные значения (например, луч на каком-то участке совпадает с контуром).
Название: Re: Точка в замкнутом контуре (кривой)
Отправлено: vermesser от 01-02-2018, 11:48:11
Для исключения граничных значений можно вместо одного луча делать так:

Если точка не на контуре, то:
Затем берем среднее между остатками от деления на 2
и если он равен 0, то точка за контуром;
если остаток от деления равен 1, то точка в контуре.

Код - vb.net [Выбрать]
  1. Imports Autodesk.AutoCAD.ApplicationServices
  2. Imports Autodesk.AutoCAD.DatabaseServices
  3. Imports Autodesk.AutoCAD.EditorInput
  4. Imports Autodesk.AutoCAD.Geometry
  5. Imports Autodesk.AutoCAD.Runtime
  6.  
  7. Public Class Commands
  8.  
  9.     Private Enum PointPosition As Integer
  10.         NA = 0
  11.         Inside = 1
  12.         Outside = 2
  13.         OnCurve = 3
  14.     End Enum
  15.  
  16.     Private Shared doc As Document
  17.     Private Shared db As Database
  18.     Private Shared ed As Editor
  19.     Private Shared ucs As Matrix3d
  20.     Private Shared pln As Plane
  21.  
  22.     Const minute As Double = 0.00029088820866572    'Угловая минута в радианах
  23.  
  24.     <CommandMethod("POINTRELOBJECT")>
  25.     Public Sub PointPositionRelativeToObject()
  26.  
  27.         doc = Core.Application.DocumentManager.MdiActiveDocument
  28.         db = doc.Database
  29.         ed = doc.Editor
  30.         ucs = ed.CurrentUserCoordinateSystem
  31.         pln = New Plane(ucs.CoordinateSystem3d.Origin, ucs.CoordinateSystem3d.Zaxis)
  32.  
  33.         Try
  34.  
  35.             Using lockDoc As DocumentLock = doc.LockDocument
  36.  
  37.                 Dim crv As Curve = Nothing
  38.                 Dim pt As Point3d = Nothing
  39.                 Dim ptPos As PointPosition = PointPosition.NA
  40.  
  41.                 Dim crvOpt As New PromptEntityOptions("")
  42.                 With crvOpt
  43.                     .AllowNone = True
  44.                     .AllowObjectOnLockedLayer = True
  45.                     .Message = vbCrLf & "Выберите объект: "
  46.                     .SetRejectMessage("Выбранный объект недопустим. ")
  47.                     .AddAllowedClass(GetType(Circle), False)
  48.                     .AddAllowedClass(GetType(Ellipse), False)
  49.                     .AddAllowedClass(GetType(Polyline), False)
  50.                 End With
  51.  
  52.                 Dim crvRes As PromptEntityResult
  53.                 Do
  54.                     crvRes = ed.GetEntity(crvOpt)
  55.                     Select Case crvRes.Status
  56.                         Case PromptStatus.OK
  57.                             Using tr As Transaction = db.TransactionManager.StartTransaction
  58.                                 crv = crvRes.ObjectId.GetObject(OpenMode.ForRead)
  59.                                 tr.Commit()
  60.                             End Using
  61.                             Exit Do
  62.                         Case PromptStatus.None
  63.                             ed.WriteMessage(vbCrLf & "Ничего не выбрано. ")
  64.                             Continue Do
  65.                         Case PromptStatus.Cancel
  66.                             ed.WriteMessage(vbCrLf & "Операция прервана пользователем. ")
  67.                             Exit Try
  68.                         Case Else
  69.                             ed.WriteMessage(vbCrLf & "Непредвиденная ошибка. ")
  70.                             Exit Try
  71.                     End Select
  72.                 Loop
  73.  
  74.                 Select Case crv.GetType.Name
  75.                     Case "Circle", "Ellipse"
  76.                         'OK
  77.                     Case "Polyline"
  78.                         If Not crv.Closed Then
  79.                             ed.WriteMessage(vbCrLf & "Полилиния должна быть замкнута. ")
  80.                             Exit Try
  81.                         End If
  82.                         If CheckPolylineIntersection(crv.ObjectId) Then
  83.                             ed.WriteMessage(vbCrLf & "Полилиния не должна самопересекаться. ")
  84.                             Exit Try
  85.                         End If
  86.                 End Select
  87.  
  88.                 crv.Highlight()
  89.  
  90.                 Dim ptOpt As New PromptPointOptions("")
  91.                 With ptOpt
  92.                     .AllowNone = False
  93.                     .Message = vbCrLf & "Укажите точку: "
  94.                 End With
  95.  
  96.                 Dim ptRes As PromptPointResult = ed.GetPoint(ptOpt)
  97.                 Select Case ptRes.Status
  98.                     Case PromptStatus.OK
  99.                         pt = ptRes.Value
  100.                     Case PromptStatus.Cancel
  101.                         ed.WriteMessage(vbCrLf & "Операция прервана пользователем. ")
  102.                         crv.Unhighlight()
  103.                         Exit Try
  104.                     Case Else
  105.                         ed.WriteMessage(vbCrLf & "Непредвиденная ошибка. ")
  106.                         crv.Unhighlight()
  107.                         Exit Try
  108.                 End Select
  109.  
  110.                 Dim cPt As Point3d = crv.GetClosestPointTo(pt, False)
  111.  
  112.                 If (pt - cPt).Length <= Tolerance.Global.EqualPoint Then
  113.                     ptPos = PointPosition.OnCurve
  114.                 Else
  115.                     Dim ray As New Ray
  116.                     With ray
  117.                         .BasePoint = pt
  118.                         .SecondPoint = New Point3d(pt.X + 1, pt.Y, 0)
  119.                     End With
  120.  
  121.                     Dim pts1 As New Point3dCollection
  122.                     crv.IntersectWith(ray, Intersect.OnBothOperands, pln, pts1, IntPtr.Zero, IntPtr.Zero)
  123.  
  124.                     Dim rotAxis As Vector3d = pt.GetVectorTo(New Point3d(pt.X, pt.Y, pt.Z + 1))
  125.                     ray.TransformBy(Matrix3d.Rotation(minute, rotAxis, pt))
  126.  
  127.                     Dim pts2 As New Point3dCollection
  128.                     crv.IntersectWith(ray, Intersect.OnBothOperands, pln, pts2, IntPtr.Zero, IntPtr.Zero)
  129.  
  130.                     ray.TransformBy(Matrix3d.Rotation(minute, rotAxis, pt))
  131.  
  132.                     Dim pts3 As New Point3dCollection
  133.                     crv.IntersectWith(ray, Intersect.OnBothOperands, pln, pts3, IntPtr.Zero, IntPtr.Zero)
  134.  
  135.                     ray.Dispose()
  136.  
  137.                     Dim m = New Integer() {
  138.                         pts1.Count Mod 2,
  139.                         pts2.Count Mod 2,
  140.                         pts3.Count Mod 2
  141.                     }
  142.                     Dim av As Integer = Math.Round(m.Average, 0)
  143.  
  144.                     If av = 0 Then
  145.                         ptPos = PointPosition.Outside
  146.                     Else
  147.                         ptPos = PointPosition.Inside
  148.                     End If
  149.  
  150.                 End If
  151.  
  152.                 Select Case ptPos
  153.                     Case PointPosition.NA
  154.                         ed.WriteMessage(vbCrLf & "Не удалось определить местоположение точки относительно объекта. ")
  155.                     Case PointPosition.Inside
  156.                         ed.WriteMessage(vbCrLf & "Точка находится внутри контура объекта. ")
  157.                     Case PointPosition.Outside
  158.                         ed.WriteMessage(vbCrLf & "Точка находится вне контура объекта. ")
  159.                     Case PointPosition.OnCurve
  160.                         ed.WriteMessage(vbCrLf & "Точка находится на контуре объекта. ")
  161.                 End Select
  162.  
  163.                 crv.Unhighlight()
  164.  
  165.             End Using
  166.  
  167.         Catch acadEx As Exception
  168.  
  169.             ed.WriteMessage(vbCrLf & "Во время выполнения произошла ошибка. " & vbCrLf & acadEx.ToString)
  170.  
  171.         Catch sysEx As System.Exception
  172.  
  173.             ed.WriteMessage(vbCrLf & "Во время выполнения произошла ошибка. " & vbCrLf & sysEx.ToString)
  174.  
  175.         End Try
  176.  
  177.     End Sub
  178.  
  179. End Class