Всем добрый день! Сегодня рассмотрим экзотичную задачу на грани функционала AutoCAD 3D [3D моделирование] и ГИС в части операций над гранями (Face) 3д-тел (Solid3d) для назначения им цвета/материала в зависимости от попадания оной грани в тот или иной полигон (замкнутую полилинию).
*Рассматривать буду в AutoCAD Civil 3D из среды Dynamo.
Отметим особенности постановки условий:
- работаем с телами (солидами) средствами AutoCAD API;
- в качестве полигонов "разметки" используем классические замкнутые полилинии (из OSM);
- солид у нас единственный, правда, тяжелый - выдавленный из разряженной поверхности построенной по материалам воздушного лазерного сканирования (сиречь, точный);
- проверку попадания грани в полигон проводим инструментами QGIS.
Глобальная задача - экспортировать тело с раскрашенными зонами по типу поверхностей [используя полигоны из OSM] в IFC для использования в координационной модели города (отсылка к курсу по BIM-менеджменту).
1. Подготовка OSM-подосновы и стратегия обработки
По большому счету, нам нужны 2 векторных слоя, входящих в "классическую поставку OSM" -- это vegetation-polygon.shp, water-polygon.shp. Все они показаны ниже в границах нужной области:
Территория суши формально всё (просто очередность прорисовки слоев). То есть неразмеченные грани будут по умолчанию "сушей".
Я пробовал также включать этот слой, но процесс "зависал" из-за необходимости программе смотреть на "многоточечные" границы суши.
Самой проблемной задачей будет определить попадание точки-центроида грани в тот или иной полигон. Да, я буду апелировать не гранью в целом а только её центроидом.
Мы будем следовать принципу "в первый попавшийся полигон", то есть для оптимизации скорости не будем считать какой из полигонов "накрывает" грань в большей степени, хоть это и возможно в настройках параметров опции"Join attributes by location"
В общем случае, задача определения попадания точки в полигон - сложная задача (с позиции сложности программной - необходимости проведения большого числа итераций). Здесь же полигонов сотни и очевидно что проверять каждый центроид с каждым -- неправильный подход (какими-то самописными методами или методом из DesignScript.Polygon.ContainmentTest(Point)).
Если задача была бы определить попадание грани (центроида) в ту или иную поверхность - задача решалась бы абсолютно также (у нас полигоны - это OSM; в обозначенном случае - полигоны были бы границами поверхностей).
Условимся, что полигон в рамках некоего слоя будет обозначать свой материал. К примеру, пусть для слоя vegetation-polygon окраска будет зеленой (grass), а для water-polygon -- голубой (water). Или дополнительно разделяться в зависимости от значения тега 'NATURAL' в свойствах слоя.
Теперь наметим алгоритм действий:
Я обозначалзачеркнутымиоперации не над точками, а над полигонами-гранями (функции пока не написаны, но подразумеваются).
- Получаем набор граней тел как коллекцию центроидов (сразу вычисляем центроид как полусумму координат треугольной грани и фиксируем идентификатор каждой грани - родительское тело и порядковый номер в списке граней). При желании экспортировать всё-таки полигон
можно будет выбрать соотв. опцию(допишу потом расширение). - Создаем на базе полученных центроидов точки AutoCAD
(полилинии), назначаем им атрибутику в виде ObjectData - Экспортируем полученные точки
(полилинии)с атрибутикой в SHP-файл; - В среде QGIS проводим операцию определения попадания точек
(полигонов)в полигоны инструментом Join attributes by location и экспортируем точки(центроиды по полигонам)с новым атрибутом типа полигона обратно в Civil 3D. При этом заменяем null на другое значение (для территории суши). - Загружаем точки mapimport'ом и запускаем второй скрипт, который сперва отсортирует массив точек по спискам (применимо к солиду) и для грани каждого присвоит материал\цвет в зависимости от условия.
- Экспортируем сущности в IFC и смотрим на результаты. Или в другую среду.
К слову в IFC ни цвета, ни материалы не попадают 😰😰😰.
2. Методы обработки солидов средствами AutoCAD 3D API
2.1 Получение ObjectId тела
В силу того, что работать с объектом я буду программно, то и инициализировать мне его следует также программно изначально. Для этого я воспользуюсь своими "нод-паками", новые возможности которых я освещал в ранней статье.
Выборку объектов к слову можно производить и так, и так (если пользоваться стандартными опциями - то не забыть добавить метод GetObjectIdsByObjects_AcadObj):
2.2 Получение набора граней тела и проблемы их идентификации
Задачи с телами могут быть не только связанными с поверхностями (где "нужной" единицей тела будет треугольник). Возможно, кому-то потребуется апеллировать другими гранями - поэтому я оставлю этот выбор в виде выбора размерности грани, по которой будет произведена сортировка граней тела (этот же показатель будет использоваться для расчета центроида грани).
Здесь уже наступает черед программирования. Мы будем апеллировать сущностями Autodesk.AutoCAD.DatabaseServices.Solid3d как базового объекта тела и Autodesk.AutoCAD.BoundaryRepresentation.Face из библиотеки "acdbmgdbrep.dll", содержащей методы по работе с телами и их частями.
Для понимания логики представления 3д-тела в среде AutoCAD рекомендую эту статью от 2008 года от одного из разработчиков программы.
Основной код, который нам потребуется в целом представлен здесь. Только я разделю его на 2 части - в рамках первого метода -- получение центроидов граней тела и второго метода - назначение материалов после процедуры обработки в QGIS.
Здесь есть один важный момент - типизация данных для передачи идентификатора грани (Face) между методами (в QGIS и обратно). Нам придется передавать этот идентификатор фактически в строчном виде, следовательно, надо найти такой способ, чтобы иметь возможность вернуть его обратно в том же виде. Другая проблема - это идентификатор солида (чему принадлежит грань), причем в таком виде, чтобы последующий поиск был быстрый.
Также перед началом действий необходимо обнулить набор таблиц ObjectData чертежа (скрипт Solids_0.dyn) и установить стиль отображения точкам AutoCAD. Эти операции не вынесены в основной скрипт, так как там появлялись непонятные фаталы.
Вообще говоря вот с этой задачей я замучился ловить вылеты программы вообще без диалоговых окон об ошибках даже при программной отладке! Накладывались, видать, исключения со стороны самого AutoCAD, со стороны Map 3D (ObjectData) и со стороны Dynamo.
В отношении граней тела есть свой идентификатор - так называемый, SubentityId. Но он не вырождается в Handle. Также есть значение номера грани в рамках солида (подсказано А. Ривилисом -- face.SubentityPath.SubentId.IndexPtr). Оно как раз вырождается в long/string и может быть впоследствии идентифицировано из строчного вида (мой основной текущий способ работы).
Я пробовал обходной путь, а именно пользовался координатами центроида грани как идентификатором (округленные к примеру до 3 знака) + Solid.Handle, но это неверно методологически.
Надо сказать, что процесс получения информации о центроидах граней довольно долгий - у меня 67000 граней обрабатывались порядка 6 минут (необходимость для каждой грани сделать запрос на входящие в неё вершины, перебрать их -- чтобы точно работать с треугольной гранью и построить центроид).
2.3 Операции в QGIS
Тут у меня есть вот такая подоснова (импортированные точки) и объединенные полигоны по водным пространствам и покрытиям:
После обработки удобным способом делаем присвоения null-параметры слово "dirt" (в моём случае - для обозначения суши).
Не показываю процесс, так как стыдно признаться что я до сих пор не научился работать с редактором выражений полей и для массовой замены экспортирую слой как редактируемый текстовый формат типа geojson, в блокноте делаю замену и загружаю обратно ...
Теперь сохраняю этот слой как SHP и импортирую в AutoCAD (Civil 3D).
2.4 Подготовка для задания цвета/материала
Для возможности присвоить граням материал, набор материалов надо предварительно в чертеже их настроить по тем именам поверхностей, которые будут использоваться.
В этом случае -- заходите в диалоговое окно MATERIALS и дерзайте.
В случае, если вы хотите назначить цвет -- здесь ситуация попроще. Вам надо также сопоставить (словарем) строчный идентификатор материала и код цвета AutoCAD (AutoCAD.Colors.Color). Я позже сделаю транслятор между цветами Dynamo и цветами AutoCAD.
2.5 Процесс присвоения данных граням
Некоторые особенности расписал ниже.
3. О трудностях, логике, жизни и смерти
Стоит отметить то, что эти строки я пишу за 1 час до Нового года использование в нодах Dynamo методов, обращающихся к внутренностям программы (database) в рамках транзакций -- крайне нестабильная процедура, вызывающая мгновенный вылет программы без фатальных ошибок. И потому требует дальнейшего изучения ...
Я решил сделать проще для всех (но сложнее для себя), сделав возможность пользователю указывать текущий документ для нода (см. картинку выше - первый input). -- что собственно и породило ошибку ниже.
Казалось бы, невинная операция, теоретически равноценная Application.DocumentManager.MdiActiveDocument, но именно с этого момента у меня начались случаи одновременного обращения к БД чертежа, что приводило к вылету. Чтобы это не допускать, можно либо не выводить в input задание документа (а использовать Document doc = Application.DocumentManager.MdiActiveDocument) внутри метода, либо "проводками" связывать Document.Current только с одним методом (нодом).
Вишенка ещё состоит в том, что не все чужие методы, работающие с database чертежа гарантированно используют классическое обращение к текущему документу, а также могут использовать этот глючный Autodesk.AutoCAD.DynamoNodes.Document, что также не добавит стабильности скрипту.
Также! Я неоднократно использовал замечательный функционал ObjectData. И если создавать через Dynamo таблицы, которые уже есть в модели также последует вылет (не уверен, но вероятно).
Для ObjectData я пользуюсь нодами из пакета Civil3DToolkit, а они согласно де-комилляции не содержат проверку на существование в документе таблиц с тем же именем (либо этого не содержит внутренний метод API Map 3D).
Теперь что по поводу идентификаторов. Пока что медленное абсолютно всё! Работающее - перебор словаря идентификаторов граней (в методе, который назначает цвет/материал граням солида -- у меня это "SetMaterialByFacesCentroids"). Явная идентификация ещё медленней (раза в полтора). Возможно я что-то делаю не так ... но это действительно похоже на правду - поиск в БД солида конкретных позиций ... что может приводить к выводу об отсутствии там индексации (объяснение медленного поиска).
OneSolidId.Handle_face.SubentityPath.SubentId.IndexPtr.ToInt64()
Таким образом, вот такой (см. выше) идентификатор -- вполне рабочее решение в виде словаря, но не для прямого использования.
4. Перспективы использования?
В IFC цвета и материалы не передаются (экспортом из Civil 3D); толку от такого тела нету. Разве что передавать в Navisworks ... да и всё.
Телу целиком, кстати, можно назначить и цвет (в IFC идет) и материал.
Засим, выкладывают скрипты "as is" - разбирайтесь кому надо, пишите если не получится, буду пробовать помочь ....
Не пропускайте публикации, подписывайтесь на Telegram-канал с тизерами статей.
#autocad #dynamo #autodesk dynamo #civil 3d #autocad api #solid #face #brep