Сортировка коллекции точек, входящих в 3-d полилинию

Автор Тема: Сортировка коллекции точек, входящих в 3-d полилинию  (Прочитано 3967 раз)

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

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

  • ADN OPEN
  • Сообщений: 10
  • Карма: 0
Сортировка точек 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.  
В итоге у меня постоянно ловится ошибка «Невозможно сохранить объект в массиве этого типа»

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



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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Если Вы только начали программировать, то рекомендую сразу отказаться от VB.NET в пользу C#.
Код комментировать не буду, так как по моему это одна сплошная ошибка.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Для вычисления расстояния между точками (Point3d) не нужно создавать отрезок (Line).
Расстояние между точками вычисляется при помощи метода DistanceTo, т.е.:
Код - vb.net [Выбрать]
  1. Dim S1 As Double = p1.DistanceTo(p2)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN OPEN
  • Сообщений: 10
  • Карма: 0
Если Вы только начали программировать, то рекомендую сразу отказаться от VB.NET в пользу C#.
Мне лично vb.net помог быстрее почувствовать "практическую радость от программирования". Я работаю инженером-геодезистом, в сфере изысканий на объектах ж-д транспорта, от программирования был совсем далек. Для улучшения своей производительности труда, пока было свободное время на ковид-выздоровлении, нашел видеоуроки на udemy.com по созданию плагинов для Autocad на vb.net, и изучил их. Для моих задач пока хватало материалов оттуда и официального developer's guid от Autodesk, я делаю для своей работы всякие полезные функции на vb.net касательно быстрого создания и редактирования объектов чертежа, экспорта данных в сторонний софт. И сейчас бьюсь над задачей создания чертежа поперечного профиля съемки на основании 3-d полилинии сечения (аналогично "сечению" в civil 3d).
А так я полностью согласен с Вами насчёт необходимости перехода сразу на С#, уже на самой ранней стадии столкнулся с тем, что практически все полезные примеры для меня написаны на C#, и я уже потихоньку и так могу готовый пример на C# адаптировать себе (сделал для своего плагина свою ленту с панелями и кнопками).

Ну а насчёт кода - да, "заблудился" совсем что-то...

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Я может не совсем правильно понял задачу, но мне кажется, что автор что то намудрил, очень сильно намудрил...
Мне видится, что там должно быть что то типа:
Код - 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.     }

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

  • ADN OPEN
  • Сообщений: 10
  • Карма: 0
Я нашел выход из сложившейся ситуации и привожу ниже свой код - вдруг кто-нибудь может дать дельный совет. А так - он у меня работает как надо и на основании отсортированной коллекции создается "правильная" 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

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Зачем в вашем коде транзакция? Вы же ничего не добавляете в базу чертежа.
Зачем Try-Catch?
На 11 строчке Вы опять вычисляете расстояние через создание линии, хотя Александр Вам написал как нужно, более того, если Вы не хотите использовать встроенные методы, то вспомните векторную алгебру, нахождение длины вектора

И хоть у меня кровь из глаз идет, когда я на 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

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

  • ADN OPEN
  • Сообщений: 10
  • Карма: 0
Владимир Шу, Владимир Шу,
Владимир, спасибо большое за совет - буду осознавать его.
Насчёт моего метода вычисления расстояния в 10 строке - это я написал себе функцию по вычислению горизонтального проложения линии (расстояния на плоскости), так как боюсь, что функция DistanceTo выдаст наклонное расстояние, так как у нее аргумент point3d.
А так - еще раз Вам огромная благодарность за советы, буду изучать и подтягивать уровень.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Насчёт моего метода вычисления расстояния в 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 - это плохой метод.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
вычислению горизонтального проложения линии (расстояния на плоскости),
А так не проще, вместо 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())

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