Задачи, связанные с "поднятием" 3D-модели по плоской топооснове типичны для любого Проекта. Как правило, подобные действия выполняются на старте проекта при получении исходных данных по участку проектирования. Если изыскания выполняются по тех-заданию, то к формату итоговых данных можно изначально выдвинуть требования, а вот если топооснова выдается отдельными уполномоченными органами/приобретается у них на коммерческой основе - то, зачастую, формат данных далек от возможности его дальнейшего использования, и требуются значительные трудозатраты на преобразование таких данных.
Как правило, подобные данные, в первоисточнике, хранятся в виде некой базы данных/базы векторных слоев по жесткой системе наименования и геометрии. Это может послужить на пользу при поиске отдельных частных случаев при оцифровке подобных данных.
Существенно облегчает задачу предварительная индексация содержимого чертежа. Этот вопрос я специально вынес в отдельную статью и опубликовал как отдельный набор кода - обязательно сперва читайте статью про RTree деревья.
Мы попробуем коснуться не только вопросов приведения топоосновы в божеский "плоский" вид, но и полуавтоматической оцифровки в 3D (в рамках этой серии статей).
Вопросу оцифровки отдельных слоев данных, к примеру точек поверхности было посвящено видео коллеги из CSD Юрия Сорокина.
Эту часть мы посвятим вопросу оцифровки выносок с текстом, представленных набором отдельных примитивов: двумя отрезками. имитирующими острие стрелки, полку выноски с текстом (Text) и линии-соединитель полки и точки вставки выноски.
Статью разобьем на 2 части - вопросу идентификации геометрии каждой выноски, и генерации Мультивыноски (MLeader) на месте каждого объекта геометрии.
1. Поиск частных случаев и постановка задачи
Если бы мы говорили про данных из децентрализованной системы (например. отдельные "творения" отдельных лиц и организаций), там как правило превалирует ручная работа и найти что-то одинаковое было бы сложно.
Вообще говоря, общая концепция выглядела бы как набор последовательных процедур:
- поиск двух пересекающихся отрезков малой длины (острие стрелки) в пределах допуска ~5 мм (поправка на корявость соединения AutoCAD'ом концов линий);
- поиск третьей линии, входящей в точку пересечения данных отрезков; к слову - эта точка пересечения отрезкой и будет точкой выноски;
- поиск четвертой линии -- полки выноски, конец которой связан с третьей, а второй конец свободен или имеет связанный со вторым/первым концом Text -- тут ситуация будет требовать скорее всего ручного контроля;
- индексация всех перечисленных выше объектов в "словарь" с отбором ключевых точек объекта выноски - точка вставки и точка "полки" со значением Text.
В нашем случае ситуация упрощается наличием отдельных частных случаев:
- острие стрелки - это 2 отрезка фиксированной длины, имеющие одно начало
- расположение текста на полке - строго параллельно ему (угол поворота), также расстояние от полки до точки привязки текста = фиксированному значению (разумеется, значение дробное (скрытое точностью), но одинаковое).
Таким образом, алгоритм будет выглядеть следующим образом:
- Оптимизируем содержимое чертежа
- Ищем отрезки, имеющие равную (здесь уникальный случай, когда мы можем говорить о равенстве дробных значений, что в обычной практике программирования невозможная вещь) координату в точке начала -- это будет острие выноски. Фиксируем точки острия в отдельный список;
- Делаем предварительный выбор окружностью из точки выноски, сортируем прочие объекты кроме данных отрезков с целью установления линии до полки, которая будет лежать между отрезками;
- В рамках предыдущего же выбора данных выбираем полку и текст по условию взаимнорасположения текста и полки (читай выше)
- Заносим точки геометрии выноски и содержимое текста в словарь
- Создаем выноски по всем точкам (операция могла бы быть и в составе п.5; здесь мы лишь делаем "прогиб" в сторону логики Dynamo для препятствования затирки данных).
При этом в качестве методом отбора данных будем пользоваться боевыми сценариями работы с деревьями RTree (ещё раз сошлюсь на свою раннюю статью).
То есть сперва произведем очистку чертежа, а затем - уже будем пользоваться RTree для поиска фрагментов выносок:
Такой скрипт "DeleteObjects.dyn" на файле "DemoDrawing.dwg"
Теперь перейдем к практике в Dynamo (для создания мультивыносок я воспользуюсь методами AutoCAD .NET API, которые подключу через свой пакет Dynamo Civil3D.CustomNodes).
2. О предварительной оптимизации геометрии чертежа и выборки данных
2.1 Теоретическая подготовка
Несмотря на применяемый программный подход, всегда следует максимально упрощать методику решения (формировать алгоритмы) и ограничивать диапазон рабочих данных.
В нашем случае, отрезками представлена практически вся геометрия!
Применяя наш алгоритм выборки геометрии выносок мы рискуем уйти в бесконечный процесс, поэтому критически важно сперва максимально упростить себе жизнь, отобрав, во-первых, только нужную геометрию, а во-вторых -- удалив, соответственно, ненужную.
Заметим, что все "буквы" и прочий мусор имеют фиксированный скелет геометрии (включая и направления сегментов), поэтому наш путь решения задачи -- получить все элементы геометрии (отрезки) внутри и на границе заданного контура (прямоугольник), построенного от вершинах линий.
При этом для отсечения линий коммуникаций (не "буквы"), мы сделаем ограничение на максимальную длину линий.
Конечно, здесь речь будет идти о необходимости вручную задавать правила обработки всех исключений. То есть мы подстраиваемся под Поставщика данных (геоподосновы); это оправдано, если данные от Поставщика поступают регулярно и необходимо их обрабатывать в сжатые сроки без физических терзаний.
2.2 Логика работы скрипта на удаление мусорных данных
Здесь мы наверное вынесем все операции в отдельный скрипт. Тогда его логика будет строиться следующим образом:
- Создаются условия для выборки объектов;
- Производится выборка объектов с получением их ObjectId;
- Производится фильтрация объектов по набору условий
- Производится удаление объектов
Процедуры 3 и 4 у меня реализованы в отдельном методе
Теперь, избавившись от мусора перейдем непосредственно к выборе самих структур мультивселенных мультивыносок.
3. Получаем геометрию выносок
3.1 Поиск начала выноски
Тут мне пришлось написать новый метод в классе Selection.GetLinesByLength() возвращающий набор отрезков, удовлетворяющий заданному условию длин.
3.2 Поиск линии-соединителя полки и точки выноски
Теперь необходимо последовательно обработать каждую точку выноски - то есть получить набор отрезков, лежащий рядом с данными стрелками (за исключением, конечно их самих) и проверить каждую из линий на факт прохождения между отрезками -- тогда линия гарантированно будет соединительной между полкой выноски и точкой выноски.
Сперва я немного изменил PythonScript, делающий выборку точек начала, а именно - добавил в него фиксацию для каждой точки вставки выноски коллекцию.
Теперь применим нод GetObjects_Intersects для поиска отрезков, пересекаемых условной буферной зоной вокруг точки вставки выноски:
Упс-ссс, забыл что это надо делать вот так (я намеренно уходил от прямого использования точек Dynamo):
Далее нам необходимо придумать условие проверки, как будет определяться линия. Я сформулирую его так:
- Среди списка значений мы удаляем те, чьи ObjectId = ObjectId от отрезков стрелки.
- Для каждой из оставшихся линий находим точку, отстоящую на малое расстояние от начала выноски (порядка 0,01м) и проверяем, входит ли она в треугольник, образованный точкой вставки и крайними точками отрезков-стрелки.
- Если проверка выполняется - то цикл останавливается и возвращается ObjectId этой линии и сразу же - конечная координата этой линии, отличная от точки вставки - для запуска следующего метода, ищущего полку и связанный с ней объект Text.
Мне честно сказать было западло писать это через Python Script, и я сделал эту проверку отдельным методом в рамках нового класса Civil3D_CustomNodes.TopoRecognize, а метод называется GetLineIdBetweenLines. Он принимает на вход текущую точку вставки выноски и 2 списка -- с объектами стрелки и прочими линиями, полученными посредством запроса Intersects, а также буфером поиска новой линии - 0.01 (в целом, этого должно хватать всегда).
Далее необходимо найти линию полки и связанный с ней текст.
По окончанию я сохраняю результаты в список:
Стоит отметить удобство простановки COGO-точек - по ним (индексам +1) удобно отслеживать неполадки распознавания элементов в коде.
4. Реализуем класс с выносками
Выноски (они же MLeader) в AutoCAD - это параметрические объекты, имеющие свой отдельный класс в API. В нодпаке Civil3DToolkit есть ряд инструментов по их созданию, но все они опираются на какой-то объект, тогда как у нас есть в наличии только точки.
Я воспользуюсь самописным методом, для создания которого обращуюсь к этому замечательному примеру (и этому).
Такой скрипт "CreateMLeaders.dyn" на файле "DemoDrawing.dwg"
https://disk.yandex.ru/d/ttWIccmIDvDSqg
Используемые пакеты нодов Civil3D.CustomNodes и DynamoRTree обновил.
5. Небольшое послевкусие послесловие
О чем хочется сказать? Я теперь вплотную уперся в сложность/невозможность как написания, так и отладки скриптов в среде Dynamo с использованием встроенных Python - скриптов -- потому и заменил часть операций выше на отдельные ноды написанные на c#.
Всё-таки когда дело касается обработки неродных объектов для AutoCAD и Dynamo, наступает много сложностей от программной реализации Dynamo (и его конфликтов с классическими функциями языка) до прямой типизации элементов (те же мои метания между DesignScript.Geometry.Point и DatabaseServices.Point3d). Используя динамически-типизированный Python, который гибко связан как с DesignScript, так и с .NET (и COM) библиотеками AutoCAD мы игнорировали части этих моментов, но для статически-типизированного C# так поступать уже не можем.
К чему я веду? К тому, что наверное далее я не буду рассматривать реализацию столь экзотичных действий в среде Dynamo, а буду сразу писать кодом (код, к слову, лежит на GitHub). Борьба с логикой Dynamo порой вообще удручает и заниматься этим дальше желание пропадает напрочь ...
Если в этой части мы рассмотрели ситуацию с мультивыносками, то в следующей соединить отдельные участки линий в единое целое.
Кстати похожие действия как я делал с выносками -- можно сделать для расчлененки блоков - вставить сам блок на месте одного из объектов (с постоянной длиной), а остальные куски удалить вообще.
Не пропускайте публикации, подписывайтесь на Telegram-канал с тизерами статей.
#autocad #dynamo #autodesk dynamo #civil 3d #autocad api #mleader #rtree #топооснова #генплан