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

ADN Club => AutoCAD .NET API => Тема начата: Андрей Афонин от 18-06-2022, 18:13:31

Название: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Андрей Афонин от 18-06-2022, 18:13:31
Сортировка точек 3-d полилинии по возрастанию.
Добрый день! Прошу помощи с кодом, я совсем только на начальном уровне нахожусь.
Дано: имеется в чертеже исходная 3-d полилиния, у которой вершины расположены не по «порядку», то есть не по мере удаления от начальной точки.
Задача – получить 3-d полилинию, у которой исходные вершины будут расположены по порядку (иллюстрация ниже).
 
Для этого пытаюсь создать функцию, которая будет сортировать коллекцию исходных 3-д точек, входящих в полилинию, по мере увеличения расстояния от каждой точки до начальной точки. И строить 3-д полилинию по новой отсортированной коллекции.
Пробую использовать метод Sort класса System.Array
Далее привожу свой код на vb.net.
Код - vb.net [Выбрать]
  1. Public Function SortColArray(myPolyline3dcol As Point3dCollection) As Point3dCollection
  2.             Dim SortCol As New Point3dCollection ' это коллекция для отсортированных по порядку точек
  3.             Dim i, j As Integer ' это переменные для счётчика
  4.             Dim S1 As Double  ' это переменная для расстояния от каждой точки до начальной
  5.  
  6.             '___________________Объявляем массив из индексов точек и соответствующих им расстояний до стартовой точки
  7.             Dim ArrayOfDist(S1) As Double  ' массив для вычисленных расстояний
  8.  
  9.             Dim ArrayOfPoints(i) As Point3d  'это я пробую объявить массив для 3-д точек
  10.             '________________________Начинаем обработку с улавливанием ошибок_____________________________________
  11.             Try
  12.                 For Each my3dpoint As Point3d In myPolyline3dcol 'перебираю каждую точку в исходной несортированной коллекции
  13.                     For i = 0 To myPolyline3dcol.Count - 1 ' открываю цикл для индексов каждой найденной точки
  14.                         S1 = Vychisli_S(myPolyline3dcol(0), my3dpoint) 'вычисляю расстояние от найденной точки до начальной точки исходной коллекции
  15.                         'в отдельной функции, это место работает легко в других моих программах
  16.                         ArrayOfDist.SetValue(S1, i) 'добавляем вычисленное расстояние в свой массив с текущим индексом
  17.                         ArrayOfPoints.SetValue(my3dpoint(i), i)  'здесь я пытаюсь закинуть 3-d точку из коллекции в ранее объявленный массив для 3-д точек.
  18.  
  19.                     Next
  20.                 Next
  21.                 Array.Sort(ArrayOfDist, ArrayOfPoints) 'здесь и должна происходить, как мне кажется, сортировка массива ArrayOfPoints по ключам, содержащимся в массиве ArrayOfDist
  22.                 ' то есть каждому элементу массива точек ArrayOfPoints соответствует свой элемент в массиве расстояний ArrayOfDist. И сортируя по возрастанию массив ArrayOfDist,
  23.                 ' я надеюсь параллельно "расставить по местам" точки в массиве ArrayOfPoints
  24.                 '_________________________________________далее я пытаюсь добавить каждый элемент массива ArrayOfPoints в коллекцию Point3dCollection
  25.                 For j = 0 To ArrayOfPoints.Length - 1
  26.                     'SortCol.Add(myPolyline3dcol(ArrayOfIndex(i)))
  27.                     SortCol(j) = ArrayOfPoints(j)
  28.                 Next
  29.             Catch ex As system.Exception
  30.                 ed.WriteMessage("Что-то пошло не так..." & ex.Message) '
  31.             End Try
  32.             Return SortCol ' тут я возвращаю отсортированную коллекцию, по которой в моем Sub строится и добавляется в чертеж правильная 3-d полилиния, но.....
  33.         End Function
  34. Public Function Vychisli_S(p1 As Point3d, p2 As Point3d) As Double
  35.             Dim line1 As Line = New Line(New Point3d(p1.X, p1.Y, 0), New Point3d(p2.X, p2.Y, 0))
  36.             Dim S1 As Double = line1.Length
  37.             Return S1
  38.         End Function
  39.  
В итоге у меня постоянно ловится ошибка «Невозможно сохранить объект в массиве этого типа»

Помогите, пожалуйста, найти решение.

(https://i.postimg.cc/QH5Zm0hS/image.jpg) (https://postimg.cc/QH5Zm0hS)
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Александр Ривилис от 18-06-2022, 18:31:58
Если Вы только начали программировать, то рекомендую сразу отказаться от VB.NET в пользу C#.
Код комментировать не буду, так как по моему это одна сплошная ошибка.
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Александр Ривилис от 18-06-2022, 18:36:30
Для вычисления расстояния между точками (Point3d) не нужно создавать отрезок (Line).
Расстояние между точками вычисляется при помощи метода DistanceTo, т.е.:
Код - vb.net [Выбрать]
  1. Dim S1 As Double = p1.DistanceTo(p2)
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Андрей Афонин от 18-06-2022, 19:33:47
Если Вы только начали программировать, то рекомендую сразу отказаться от VB.NET в пользу C#.
Мне лично vb.net помог быстрее почувствовать "практическую радость от программирования". Я работаю инженером-геодезистом, в сфере изысканий на объектах ж-д транспорта, от программирования был совсем далек. Для улучшения своей производительности труда, пока было свободное время на ковид-выздоровлении, нашел видеоуроки на udemy.com по созданию плагинов для Autocad на vb.net, и изучил их. Для моих задач пока хватало материалов оттуда и официального developer's guid от Autodesk, я делаю для своей работы всякие полезные функции на vb.net касательно быстрого создания и редактирования объектов чертежа, экспорта данных в сторонний софт. И сейчас бьюсь над задачей создания чертежа поперечного профиля съемки на основании 3-d полилинии сечения (аналогично "сечению" в civil 3d).
А так я полностью согласен с Вами насчёт необходимости перехода сразу на С#, уже на самой ранней стадии столкнулся с тем, что практически все полезные примеры для меня написаны на C#, и я уже потихоньку и так могу готовый пример на C# адаптировать себе (сделал для своего плагина свою ленту с панелями и кнопками).

Ну а насчёт кода - да, "заблудился" совсем что-то...
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Владимир Шу от 19-06-2022, 14:16:29
Я может не совсем правильно понял задачу, но мне кажется, что автор что то намудрил, очень сильно намудрил...
Мне видится, что там должно быть что то типа:
Код - C# [Выбрать]
  1.     public static Gem.Point3dCollection SortColArray(Gem.Point3dCollection points)
  2.     {
  3.       var point3Ds = new Gem.Point3d[points.Count];
  4.       points.CopyTo(point3Ds, 0);
  5.       return new Gem.Point3dCollection(point3Ds.OrderBy(q => q.DistanceTo(point3Ds[0])).ToArray());
  6.     }
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Андрей Афонин от 19-06-2022, 21:10:11
Я нашел выход из сложившейся ситуации и привожу ниже свой код - вдруг кто-нибудь может дать дельный совет. А так - он у меня работает как надо и на основании отсортированной коллекции создается "правильная" 3-d полилиния вместо "запутанной".

Код - vb.net [Выбрать]
  1.  Public Function SortPoint3dCollection(GotovyPoper3dCol As Point3dCollection) As Point3dCollection
  2.             Dim SortCol As New Point3dCollection
  3.             Dim i, j As Integer
  4.             Dim S1 As Double
  5.             Dim ArrayOfDist(GotovyPoper3dCol.Count - 1) As Double
  6.             Dim ArrayOfIndex(GotovyPoper3dCol.Count - 1) As Integer
  7.             Using Trans As Transaction = db.TransactionManager.StartTransaction() ' начинаем транзакцию
  8.                 Try
  9.                     For i = 0 To GotovyPoper3dCol.Count - 1
  10.                         S1 = Vychisli_S(GotovyPoper3dCol.Item(0), GotovyPoper3dCol.Item(i))
  11.                         ArrayOfDist.SetValue(S1, i)
  12.                         ArrayOfIndex.SetValue(i, i)
  13.                     Next
  14.                     Array.Sort(ArrayOfDist, ArrayOfIndex)
  15.                     For j = 0 To GotovyPoper3dCol.Count - 1
  16.                         SortCol.Add(GotovyPoper3dCol.Item(ArrayOfIndex(j)))
  17.                     Next
  18.                 Catch ex As System.Exception
  19.                     ed.WriteMessage("Что-то пошло не так...." & ex.Message)
  20.                 End Try
  21.             End Using
  22.             Return SortCol
  23.         End Function
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Владимир Шу от 19-06-2022, 21:53:51
Зачем в вашем коде транзакция? Вы же ничего не добавляете в базу чертежа.
Зачем Try-Catch?
На 11 строчке Вы опять вычисляете расстояние через создание линии, хотя Александр Вам написал как нужно, более того, если Вы не хотите использовать встроенные методы, то вспомните векторную алгебру, нахождение длины вектора (http://www.cleverstudents.ru/vectors/vector_length.html)

И хоть у меня кровь из глаз идет, когда я на VB пишу, я все таки попробую изобразить...
Код - vb.net [Выбрать]
  1.     Public Function SortPoint3dCollection(GotovyPoper3dCol As Point3dCollection) As Point3dCollection
  2.         Dim p(GotovyPoper3dCol.Count) As Point3d
  3.         'Просто копируем точки из коллекции в массив, что бы можно было использовать LINQ
  4.         GotovyPoper3dCol.CopyTo(p, 0)
  5.         'Сначала поместим в начало списка точку с наименьшими координатами
  6.         p = p.OrderBy(Function(k) k.X).ThenBy(Function(k) k.Y).ToArray()
  7.         'а тотом отсортирует точки по расстаянию от точки с наименьшими координатами
  8.         Return New Point3dCollection(p.OrderBy(Function(k) k.DistanceTo(p(0))).ToArray())
  9.     End Function
ЗЫ. Я этот код не запускал, но студия вроде не ругается и даже собирает...

На всякий случай, то же самое в VB можно записать немного по другому:
Код - vb.net [Выбрать]
  1.         Dim orderByXY = From s In p Order By s.X, s.Y Select s
  2.         Dim orderByDist = From s In p Order By s.DistanceTo(p(0)) Select s
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Андрей Афонин от 19-06-2022, 22:12:24
Владимир Шу, Владимир Шу,
Владимир, спасибо большое за совет - буду осознавать его.
Насчёт моего метода вычисления расстояния в 10 строке - это я написал себе функцию по вычислению горизонтального проложения линии (расстояния на плоскости), так как боюсь, что функция DistanceTo выдаст наклонное расстояние, так как у нее аргумент point3d.
А так - еще раз Вам огромная благодарность за советы, буду изучать и подтягивать уровень.
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Александр Ривилис от 19-06-2022, 22:20:02
Насчёт моего метода вычисления расстояния в 10 строке - это я написал себе функцию по вычислению горизонтального проложения линии (расстояния на плоскости), так как боюсь, что функция DistanceTo выдаст наклонное расстояние, так как у нее аргумент point3d.
Если Z отличается, то действительно расстояние будет не по плоскости. Но достаточно использовать точки с одинаковым Z. Т.е.
Код - vb.net [Выбрать]
  1. p1 = new Point3d(p1.x, p1.x, 0)
  2. p2 = new Point3d(p2.x, p2.x, 0)
А использовать Line - это плохой метод.
Название: Re: Сортировка коллекции точек, входящих в 3-d полилинию
Отправлено: Владимир Шу от 19-06-2022, 23:02:31
вычислению горизонтального проложения линии (расстояния на плоскости),
А так не проще, вместо DistanceTo(), записать формулу вычисления длины вектора, явно указав какие координаты использовать?
Код - vb.net [Выбрать]
  1. Return New Point3dCollection(p.OrderBy(Function(k) Math.Sqrt((k.X - p(0).X) ^ 2 + (k.Y - p(0).Y) ^ 2)).ToArray())

Хотя для красоты, я бы метод расширения для точки дописал бы с этой же формулой, но было бы красивее...