Пересечение между плоскостью и кривой в .NET

Автор Тема: Пересечение между плоскостью и кривой в .NET  (Прочитано 8773 раз)

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

Оффлайн Александр РивилисАвтор темы

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

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
В коде использован неожиданный метод получения плоскости региона: регион взрывают и берут плоскость с первого попавшегося огрызка firstEntity.GetPlane(). И это срабатывает (я проверил), хотя в данном случае регион - это прямоугольник и плоскость взята у линии, firstEntity это Line !! Это подрывает мой школьный курс геометрии - линия не задает плоскость! :) Но этот очень полезный метод, может пригодится на каждом шагу. Если конечно понять как это работает.
Если это срабатывает тут, то могу ли всегда обращаться к GetPlane у любой Curve?
Какую именно плоскость выдаст этот метод, если я только что создал линию по 2м точкам? 
Можно ли надеяться, что плоскость взятая у линейного ребра солида - это всегда поверхность солида? И собственно какая из 2х? Ведь ребро всегда прилегает к 2м граням...

Оффлайн Александр РивилисАвтор темы

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Если это срабатывает тут, то могу ли всегда обращаться к GetPlane у любой Curve?
Нет. А вот по поводу плоскости у отрезка у нас на форуме уже было обсуждение и даже передавали запрос в ADN DevHelp. Вывод из этого общения - плоскость определяется вектором нормали (вектором выдавливания), который есть у примитива Line - так сложилось исторически: http://adn-cis.org/forum/index.php?topic=7083.msg20531#msg20531
« Последнее редактирование: 29-08-2016, 16:19:28 от Александр Ривилис »
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Стоит наверно отметить для тех, кто захочет использовать этот код, что указанный способ поиска пересечения ищет точки не только на кривой, но и на ее виртуальных продолжениях. Если надо искать только реальные точки, то вместо Intersect.ExtendBoth надо писать Intersect.OnBothOperands.

Оффлайн Александр РивилисАвтор темы

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Код в статье исправил. Для эллипса (как и для любой другой замкнутой кривой) это исправление было непринципиально и поэтому на него не обратили внимания.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Только у меня на сплайнах не работает? IntersectWith сплайна со сплайном выдает ноль точек пересечения. Не всегда, но регулярно. А спроецированная на плоскость кривая - она как раз сплайн. Даже если беру точку на кривой, строю плоскость на ЭТОЙ точке. Запрашиваю пересечения - получаю банан :(

Оффлайн Александр РивилисАвтор темы

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
К сожалению AutoCAD не всегда корректно работает со сплайнами. Чтобы диагностировать проблему как обычно нужны следующие вещи:
1. dwg-файл для примера
2. код примера
3. воспроизводимость ошибки в последней версии AutoCAD.
В этом случае можно отослать в Autodesk.
В качестве workaround могу посоветовать выполнять аппроксимацию сплайна и находить пересечение с аппроксимированной 3D-полилинией построенной по точкам аппроксимации сплайна.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
В моем частном случае плоскость кривой обычно перпендикулярна плоскости с которой надо найти пересечение, а это значит, что проекцию можно заменить на линию. Получается такой код:
Код - C# [Выбрать]
  1.         /// <summary>
  2.     /// Поиск пересечения плоскости и кривой (без учета виртуальных продолжений кривой)
  3.     /// </summary>
  4.     public static Point3dCollection IntersectWith(this Plane plane, Curve curve)
  5.     {
  6.       Point3dCollection ret = new Point3dCollection();
  7.       if (plane == null || curve == null || curve.IsErased) return ret;
  8.       Curve projectedCurve = curve.GetProjectedCurve(plane, plane.Normal);
  9.       projectedCurve.IntersectWith(curve, Intersect.OnBothOperands, ret, IntPtr.Zero, IntPtr.Zero);
  10.       Point3dCollection ret1 = new Point3dCollection();
  11.  
  12.       // Спроецированный сплайн может пройти через точку несколько раз туда-обратно -> надо удалить копии точки
  13.       foreach(Point3d p in ret)
  14.       {
  15.         bool contain = false;
  16.         foreach(Point3d old in ret1)
  17.           if(old.Approx()==p.Approx())
  18.             { contain = true;  break; }
  19.         if (!contain) ret1.Add(p);
  20.       }
  21.       ret.Dispose();
  22.       ret = ret1;
  23.  
  24.       if (ret.Count == 0) // не факт, что это правда. Возможно глюк работы со сплайнами. Проверим
  25.       {
  26.         if (curve.IsPlanar)
  27.         {
  28.           using (Plane curvePlane = curve.GetPlane())
  29.           {
  30.             if (curvePlane.IsCoplanarTo(plane) || curvePlane.IsParallelTo(plane)) return ret;
  31.             if (!(curve is Line) && projectedCurve is Spline) // SplineIntersectWith в этом случае не работает
  32.               if (curvePlane.IsPerpendicularTo(plane)) // проекция вырождается в линию
  33.               {
  34.                 Line aline = projectedCurve.ApproxLine();
  35.                 // ищем на продолжениях проекции, т.к. проекция может быть длиннее, чем расстояние от начальной до конечной точки
  36.                 aline.IntersectWith(curve, Intersect.ExtendThis, ret, IntPtr.Zero, IntPtr.Zero);
  37.                 aline.Dispose();
  38.               }
  39.           }
  40.         }
  41.         if (ret.Count == 0)
  42.           // с линией не получилось. проверим хотя бы одну точку на плоскости
  43.           try // Curve.GetClosestPointTo тоже не работает со сплайнами, поэтому вызываем исключение в GetParameterAtPoint
  44.           {
  45.             double param = curve.GetParameterAtPoint(plane.PointOnPlane); // Если нет исключения, то точка на кривой
  46.             ret.Add(plane.PointOnPlane);
  47.           }
  48.           catch { }
  49.       }
  50.       projectedCurve.Dispose();
  51.       return ret;
  52.     }
  53.  
  54.     public static Line ApproxLine(this Curve spline)
  55.     {
  56.        if (spline == null || spline.IsErased || !spline.IsPlanar)  return null;
  57.       Point3d start = spline.StartPoint;
  58.       Point3d end = spline.EndPoint;
  59.       if (start.Approx() == end.Approx())
  60.         end = spline.GetPointAtParameter(spline.StartParam + (spline.EndParam - spline.StartParam) / 2);
  61.       return new Line(start, end);
  62.     }
  63.  
Уже намного лучше работает. Но конечно это не панацея. А как делать аппроксимации - не знаю :(
« Последнее редактирование: 07-09-2016, 23:22:36 от avc »

Оффлайн Александр РивилисАвтор темы

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
А как делать аппроксимации - не знаю :(
1. Преобразуешь Curve в Curve3d (из Autodesk.AutoCAD.Geometry) при помощи метода Curve.GetGeCurve
2. Используя метод Curve3d.GetInterval получаем минимальное и максимальное значение параметра кривой.
3. Используя метод Curve3d.GetSamplePoints Method (double minParam, double maxParam, double maxChord) получаем коллекцию точек аппроксимации.
4. Из полученных точек можно используя класс Polyline3d (конструктор, которому передаются тип полилинии, точки вершин и тип замкнутости) получить аппроксимирующую кривую.
Хотя в этом случае мне кажется лучше будет находить пересечения между отрезком (LineSegment3d), образованным парой соседних точек из коллекции точек и плоскостью (Plane) при помощи метода IntersectWith.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Эх как все не просто. Нашел еще один изъян в этом методе.  Спроецированный сплайн может пройти через точку по одному и тому же месту несколько раз туда-обратно, поэтому метод выдаст одну и ту же точку несколько раз. Для моей программы это принципиально - нужно точно знать одно или много пересечений. Приходится удалять копии точки. А сравнивать любые точки надо конечно с округлением.
Обновил код (уже сбился со счету в который раз)
Off-Topic: показать
Вообще хотелось бы найти простой метод удаления невидимых петель на сплайнах и полилиниях. Бог его знает когда они возникают. Чаще всего из какой-нибудь экспортированной сложной геометрии, логотипы из корела и т.п., но мешают в реальной инженерной работе постоянно.

Оффлайн Александр РивилисАвтор темы

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Попробуй этот алгоритм: http://adn-cis.org/poluchenie-tochek-peresecheniya-krivoj-i-ploskosti.html
Я его выше уже предлагал, но ты его проигнорировал.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Я не то чтоб проигнорировал, оставил на потом. Тут уже частично работало и не хотелось начинать с нуля. Но подводные камни все всплывают и всплывают. Спасибо за новую статью! Теперь уж точно попробую :)