Точка в замкнутом контуре (кривой)

Автор Тема: Точка в замкнутом контуре (кривой)  (Прочитано 4919 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Здравствуйте!

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



Заранее спасибо!

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #2 : 27-01-2018, 18:06:28 »
Спасибо! Попробую оба способа и отпишусь.

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #3 : 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.  

  • BrepEntity.GetPointContainment выдаёт Brep: На контуре, если указываем точку внутри контура.
  • MPolygon вызывает ошибку eInvalidInput, если в качестве контура используем эллипс.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Точка в замкнутом контуре (кривой)
« Ответ #4 : 27-01-2018, 21:58:37 »
MPolygon вызывает ошибку eInvalidInput, если в качестве контура используем эллипс.
Так и должно быть. Этот метод работает только с полилиниями. Но эллипс можно апроксимировать и передать массив точек, а не сам примитив.
BREP не советую. У него невысокая точность.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #5 : 27-01-2018, 22:01:20 »
Но эллипс можно апроксимировать и передать массив точек, а не сам примитив.
Подскажите, как?

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #6 : 27-01-2018, 22:02:11 »
Этот метод работает только с полилиниями
С кругом ещё работает.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Точка в замкнутом контуре (кривой)
« Ответ #7 : 27-01-2018, 22:02:48 »
Вообще-то для круга и эллипса можно использовать и другие алгоритмы. Например, для круга расстояние от центра до точки не больше радиуса - точка внутри...
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Точка в замкнутом контуре (кривой)
« Ответ #8 : 27-01-2018, 22:05:03 »
Этот метод работает только с полилиниями
С кругом ещё работает.
Да. Еще кажется 2d-полилиния
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #9 : 27-01-2018, 22:06:46 »
Вообще-то для круга и эллипса можно использовать и другие алгоритмы. Например, для круга расстояние от центра до точки не больше радиуса - точка внутри...
Можно, но у меня может в итоге быть и любая несамопересекающаяся кривая.
Такая, например:



Поэтому я искал встроенный метод, работающий с замкнутыми кривыми.

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #10 : 27-01-2018, 22:37:24 »
BREP не советую. У него невысокая точность.
Возможно для моих нужд хватит. Но почему то этот метод определяет точку, находящуюся внутри контура, как находящуюся на контуре.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Точка в замкнутом контуре (кривой)
« Ответ #11 : 27-01-2018, 23:57:59 »
Но почему то этот метод определяет точку, находящуюся внутри контура, как находящуюся на контуре.
Вполне возможно, что это баг в конкретной версии AutoCAD. Рекомендую остановится на методах использующих MPolygon. После AutoCAD 2007 вроде ошибок в нём не было. Любую кривую (Curve) можно преобразовать в Curve3d при помощи метода Curve.GetGeCurve(), затем при помощи метода Curve3d.GetSamplePoints() получить массив точек, создать по ним временную Polyline и использовать для MPolygon.
 
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #12 : 28-01-2018, 00:23:07 »
Спасибо!
Пока ждал ответа решил делать так:
Проверять, принадлежит ли точка кривой,
и если нет, то проверять внутри или снаружи контура точка,
следующим методом: https://cedeela.fr/inside-a-curve.html#id1,
создавая луч из точки и проверяя количество точек пересечения луча с контуром.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Точка в замкнутом контуре (кривой)
« Ответ #13 : 28-01-2018, 00:39:59 »
Спасибо!
Пока ждал ответа решил делать так:
Проверять, принадлежит ли точка кривой,
и если нет, то проверять внутри или снаружи контура точка,
следующим методом: https://cedeela.fr/inside-a-curve.html#id1,
создавая луч из точки и проверяя количество точек пересечения луча с контуром.
Тоже возможный вариант, но нужно учесть граничные значения (например, луч на каком-то участке совпадает с контуром).
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн vermesserАвтор темы

  • ADN OPEN
  • **
  • Сообщений: 66
  • Карма: 2
Re: Точка в замкнутом контуре (кривой)
« Ответ #14 : 01-02-2018, 11:48:11 »
Для исключения граничных значений можно вместо одного луча делать так:

Если точка не на контуре, то:
  • строим луч и записываем остаток от деления количества точек пересечения на 2;
  • поворачиваем луч на 1" и и записываем остаток от деления количества точек пересечения на 2;
  • поворачиваем луч ещё на 1" и записываем остаток от деления количества точек пересечения на 2.
Затем берем среднее между остатками от деления на 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