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

ADN Club => AutoCAD .NET API => Тема начата: Александр Пекшев aka Modis от 14-01-2016, 00:43:41

Название: Перпендикуляр из точки на кривую
Отправлено: Александр Пекшев aka Modis от 14-01-2016, 00:43:41
Всем привет. Вроде я находил ответ на этот вопрос, но что-то никак не могу найти. И сам не могу придумать решение (наверно уже устал  ::) )

Задача тривиальная: имеем точку Point3D и имеем кривую Curve. Нужно провести перпендикуляр из точки на кривую
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Владимир Шу от 14-01-2016, 08:58:41
Код - C# [Выбрать]
  1. Gem.Point3d p = new Db.Line().GetClosestPointTo(new Gem.Point3d(1, 1, 0), false);
Не оно?
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Александр Ривилис от 14-01-2016, 09:38:05
Задача тривиальная:
Это тебе так только кажется. А если точка за пределами кривой и опустить перпендикуляр нельзя?
Не оно?
Сработает только если реально можно провести перпендикуляр из точки на отрезок, а не на бесконечную прямую. В противном случае это будет конечная точка отрезка, ближайшая к заданной точке.
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Владимир Шу от 14-01-2016, 09:52:13
Сработает только если реально можно провести перпендикуляр из точки на отрезок, а не на бесконечную прямую. В противном случае это будет конечная точка отрезка, ближайшая к заданной точке.
Угу, я знаю.
Есть другой путь - аналитический.
1. Узнаем какой сегмент кривой ближайший
Код - vb.net [Выбрать]
  1.                 var l = new Db.Line() as Db.Curve;
  2.                 var p1 = l.GetClosestPointTo( new Gem.Point3d(0,0,0),false);
  3.                 var l2 = l.GetParameterAtPoint(p1);
2. Выясняем прямая это или дуга.
3. Для прямой что то типа этого:
Код - vb.net [Выбрать]
  1. ' Где стырил этот код уже не помню.  
  2. Public Function perpendicular(varStart As point3d, varEnd As point3d, varBase As point3d) As point3d
  3.         '                               A                      B               C
  4.                 Dim a As point3d = varStart
  5.                 Dim b As point3d = varEnd
  6.                 Dim c As point3d = varBase
  7.                 Dim d(2) As Double
  8.                 Dim F(2) As Double
  9.                 Dim k2 As Double
  10.                
  11.                
  12.                   On Error GoTo Err_Control
  13.                 F(0) = c.x - (b.y - a.y)
  14.                 F(1) = c.y + (b.x - a.x)
  15.                 k2 = ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / ((b.x - a.x) * (F(1) - c.y) - (F(0) - c.x) * (b.y - a.y))
  16.                     d(0) = (F(0) - c.x) * k2 + c.x
  17.                     d(1) = (F(1) - c.y) * k2 + c.y
  18.                   perpendicular = New Point3d(d(0),d(1),0)
  19.                  
  20.                 Exit_here:
  21.                   Exit Function
  22.                 Err_Control:
  23.           'MsgBox Err.Description
  24.         End Function
  25.  
для дуги:
 ищем точку пересечения прямой проведенной через заданную точку и точку центра дуги и собственно дугой, так же не сложно.

ИМХО, как то так.
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Александр Ривилис от 14-01-2016, 09:54:33
2. Выясняем прямая это или дуга.
Главный прокол. А кто сказал, что это полилиния, у которой могут быть только прямые или дуговые сегменты??? А если это сплайн или эллипс?
P.S.: Кстати, в своём алгоритме ты забыл про Z.
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Владимир Шу от 14-01-2016, 10:03:17
Главный прокол. А кто сказал, что это полилиния, у которой могут быть только прямые или дуговые сегменты???
Никто не сказал, придется преобразовывать к нужному типу кривой, но ИМХО моя задача подсказать путь решения, а не предоставить готовый в продакшен код.
Код - C# [Выбрать]
  1.                 var l3 = new Db.Spline();
  2.                 var l4 = l3.ToPolyline();
P.S.: Кстати, в своём алгоритме ты забыл про Z.
не забыл, я этот код где то стырил, да и не нужна мне была Z координата, а потому она в этом коде обнулена.
По хорошему этот код нужно переписать и с использованием рабочих плоскостей, но он мне настолько давно не нужен был, что если бы не вопрос Александра, то я про него и не вспомнил бы.
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Александр Ривилис от 14-01-2016, 11:44:16
Еще один вариант (перпендикуляр на прямую):
Код - C# [Выбрать]
  1. // pStart - начальная точка отрезка
  2. // pEnd - конечная точка отрезка
  3. // p - точка от которой строится перпендинуляр
  4. Line line = new Line(pStart, pEnd);
  5. Point3d pNearest = line.GetClosestPointTo(p, true);
Хотя вместо Line можно воспользоваться Line3d.
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Александр Пекшев aka Modis от 15-01-2016, 06:19:28
Да, действительно - назвав задачу тривиальной, я немного перегнул =)

Хотя и надобность в решении у меня отпала, но интерес к вопросу не пропал
Придумал вариант решения. Пока не конечный. т.к. со сплайнами какая-то ерунда. Ну и может кто подкинет свежих идей и замечаний
Задумка в следующем:
1. Имеем кривую и точку. Сколько можно отложить перпендикуляров? Правильно - несколько!
2. Берем кривую и разбиваем ее на сегменты. За основу для разбивки берем Parameter (кстати, толком не пойму  - что это такое?! Хотел бы услышать толковое разъяснение)
3. Получаем ближайшую точку на сегменте к искомой точке методом GetClosestPointTo(Point3d givenPoint, bool extend). Тут-же вопрос - как работает extend? Что и куда продолжается?
4. Проверяем, что если соединить полученную и искомую точку, то образованный отрезок будет перпендикулярен кривой. Для этого используем касательную в точке
Вот код:
Код - C# [Выбрать]
  1. private static IEnumerable<Point3d> GetPerpendicularsFromPointOnCurve(Point3d point, Curve curve)
  2. {
  3.  
  4.     var pts = new List<Point3d>();
  5.     var curveParamPts = new Point3dCollection();
  6.     if (curve is Spline)
  7.     {
  8.         var spline = (Spline) curve;
  9.         for (var cpi = 0; cpi < spline.NumFitPoints; cpi++)
  10.             curveParamPts.Add(spline.GetFitPointAt(cpi));
  11.     }
  12.     else
  13.     {
  14.         for (var p = 0; p < curve.EndParam; p++)
  15.             curveParamPts.Add(curve.GetPointAtParameter(p));
  16.         curveParamPts.Add(curve.GetPointAtParameter(curve.EndParam));
  17.     }
  18.    
  19.     foreach (Curve splitCurve in curve.GetSplitCurves(curveParamPts))
  20.     {
  21.         var closestPoint = splitCurve.GetClosestPointTo(point, false);
  22.         var derInPoint = splitCurve.GetFirstDerivative(closestPoint);
  23.         var angel = (point - closestPoint).GetAngleTo(derInPoint);
  24.         if (Math.Abs(angel - Math.PI / 2) < Tolerance.Global.EqualPoint |
  25.             Math.Abs(angel - Math.PI) < Tolerance.Global.EqualPoint)
  26.             pts.Add(closestPoint);
  27.     }
  28.     return pts;
  29. }

Извините, вам запрещён просмотр содержимого спойлеров.

Хотелось бы послушать замечания и предложения. Особенно касаемо сплайнов (пока вариант работает не совсем корректно)
И еще вопрос: есть Curve.GetFirstDerivative(), а есть Curve.GetSecondDerivative() - В чем различия?
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Александр Ривилис от 15-01-2016, 21:39:17
За основу для разбивки берем Parameter (кстати, толком не пойму  - что это такое?! Хотел бы услышать толковое разъяснение)
Почитай в интернете что такое параметрическая кривая и параметрическая функция.
Для полилиний (кроме сглаженных) в каждой из вершин параметр равен номеру вершины и равномерно распределяется вдоль каждого сегмента (т.е. у середины первого сегмента параметр 0.5).
Для отрезка параметр и расстояние от начала отрезка - это одно и тоже. Т.е. в середине отрезка его параметр равен половине длины отрезка.
Для окружности параметр - это угол из центра в радианах от 0 до 2*PI.
Для дуги - это угол из центра дуги в радианах.
Для эллипса и эллиптической дуги параметр - это тоже угловое значение.
Для сплайна параметр это расстояние вдоль полилинии, соединяющей управляющие точки сплайна.
Для Custom Entity, унаследованного от AcDbCurve это может быть в принципе всё что угодно.
Название: Re: Перпендикуляр из точки на кривую
Отправлено: Александр Ривилис от 15-01-2016, 21:54:46
И еще вопрос: есть Curve.GetFirstDerivative(), а есть Curve.GetSecondDerivative() - В чем различия?
Первая производная и вторая производная. Первая производная - это касательная, вторая производная зависит от того, что это за кривая. Для дуги - она направлена к центру дуги. Для отрезков и линейных сегментов полилинии вторая производная - это нуль-вектор. На рисунке красные - первые производные, желтые - вторые.
(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fimg-fotki.yandex.ru%2Fget%2F68630%2F7842324.5%2F0_115f1c_2f4b11af_orig.png&hash=21ccc678f3dac4732f18f5349bf2d31c)