В этой статье я предлагаю краткий обзор библиотек геометрического моделирования с точки зрения разработчика специализированной CAD системы и делюсь опытом интеграции ядра C3D.
Если рынок «больших» программ проектирования давно поделен между несколькими крупными игроками вроде AutoCAD, SolidWorks, NX, Creo Elements и CATIA и т.п., то рынок специализированных программ проектирования всего и вся – окон и лестниц, корпусной и мягкой мебели, трубопроводов и корпусов весьма широк и динамичен. Причин для этого, на мой взгляд, две: во-первых, это высокая стоимость покупки крупной САПР и сотрудника, умеющего в ней эффективно работать. А, во-вторых, отсутствие адаптации для проектирования конкретных изделий в крупной САПР приводит к тому, что скорость проектирования специализированных изделий в них низкая.
Специализированные САПР являются ответом на указанные проблемы и перед программистом стоят два пути их создания. Первый – доработка крупной САПР с использованием предоставляемых API, плагинов и всевозможных скриптов. Этот подход не всегда оправдан, т.к. в результате стоимость САПР возрастает для пользователя (нужно платить как за большую САПР, так и за адаптацию), а требуемая квалификация инженера (а, следовательно, и затраты на его обучение и содержание) для работы с таким комбайном достаточно высоки. Второй путь – создание системы «с нуля». Этот путь, несомненно, значительно сложнее, т.к. огромный функционал нужно разработать с самого начала. Но несмотря на это он может оказаться значительно дешевле и удобнее в использовании для конечного пользователя, который и определяет успех продукта.
Создание нишевой САПР, несомненно, очень затратная разработка, в которой огромную сложность несут алгоритмы геометрического моделирования трехмерных моделей. О библиотеках которые помогут вам в решении этой задачи и пойдет речь дальше. Но прежде чем начать обзор, я хочу определить функционал, который мы хотели получить от геометрического ядра. Поскольку наша CAD предназначена для проектирования мебели, то требования сформировались следующие: возможность моделирования плоских пластин произвольной формы с вырезами, профилей с постоянным сечением, тел вращения и, так называемых гнутых панелей и операции конструктивной геометрии над этими телами, построение на основе созданных тел проекций и сечений.
С прикладной точки зрения в CAD все решения геометрического моделирования можно разделить на три группы – использующие полигональное, воксельное и граничное представление. Полигональное моделирование объектов — это генерация объектов в виде набора полигонов, как правило, треугольников, которые в дальнейшем визуализируются с помощью графических API вроде OpenGL или DirectX. Этот способ наиболее прост в реализации. Создание полигональных тел и конструктивные операции с ними можно написать самостоятельно или воспользоваться открытыми библиотеками, например, CGAL. Мы использовали такую самописную реализацию много лет и она вполне себя оправдывала: дешево и сердито. Таким образом, мы довольно долго моделировали достаточно сложные мебельные изделия, например гнутые фасады, без особых хлопот. Однако полигональное представление имеет существенные недостатки, которые в итоге и вынудили искать альтернативные решения. Недостатки эти следующие: в полигональных моделях нет информации об ограничивающих тела кривых (дуг, окружностей, сплайнов) на поверхностях, а это существенно осложняет работу конструктора, затрудняя точные построения от неплоских поверхностей (например цилиндрических, конусных, сферических, парабалоидных), снижается качество геометрии при конструктивных CSG операциях, делает невозможным создание корректных чертежей спроектированных объектов, расчет точных площадей поверхностей, объемов тел и т.п. Воксельное моделирование также достаточно специфично и не предназначено для решения типичных задач конструирования.
Альтернативным решением является моделирование объектов с помощью граничного представления BREP. BREP это представление трехмерных тел, которое описывает границу этих тел в виде набора связанных граней, а каждая грань является контуром на поверхности (например плоской, цилиндрической, сферической, конической или NURBS). Этот способ свободен от недостатков полигональных моделей (хотя использует полигональные модели как промежуточный этап для визуализации или расчетов) и именно он используется в абсолютном большинстве CAD систем. Однако математический аппарат для такого моделирования несоизмеримо сложнее, и выбор библиотек достаточно ограничен. Большинство из них написаны на С++ и некоторые имеют врапперы для использования в других языках.
Лидеры в этой области – ядра Parasolid и ACIS. Они используются в подавляющем большинстве CAD. Наряду с ними известны CGM и Granite, компоненты крупных САПР Catia и Creo Elements, которые сравнительно недавно стали доступны для лицензирования. Все они обладают мощнейшей функциональностью, хорошо оттестированы и оптимизированы. Единственный их недостаток для нашего брата – цена. Очень внушительные ежегодные выплаты вкупе с большими процентными отчислениями от каждой продажи делают создание небольших CAD на их основе дорогостоящим мероприятием.
Solids++ и SMLib – две не очень известные коммерческие библиотеки и мне не удалось найти информацию о CAD системах, построенных на их основе. Согласно описаниям на их сайтах обе они реализуют типичные для геометрических ядер операции. Насколько стабильно и надежно они работают – вопрос открытый. SMLib обладает весьма весомым преимуществом – в отличие от всех остальных коммерческих библиотек, она продается вместе с исходными текстами.
Единственная на тот момент доступная отечественная разработка – ядро C3D, разработанное в АСКОН для Компаса. В момент выбора оно только выходило на рынок и заинтересовало сбалансированным для нас соотношением цены и функционала. В момент выбора ядра на горизонте также появился RGK – амбициозный отечественный проект, статьи о котором уже появлялись на хабре http://habrahabr.ru/post/180455/ и http://habrahabr.ru/post/180707/, однако спустя несколько лет никаких данных о нем невозможно получить даже на официальном сайте. А официальная почта не отвечает на письма.
Последнее рассматриваемое ядро – OpenCASCADE – единственное бесплатное решение. На его основе создан известный пакет FreeCAD. Ядро, несомненно, обладает развитым функционалом и, на мой взгляд, достаточно удобным API, но, к сожалению, и достаточно большим количеством недоработок, которые при тестировании выразились в виде неправильной геометрии при операциях вычитания тел с совпадающими гранями и ошибках генерации проекций и силуэтов деталей. Однако не стоит забывать, что это открытый проект, в котором при возможности вы можете поправить ошибки самостоятельно. Это ядро можно использовать в коммерческих продуктах совершенно бесплатно, но не стоит забывать, что, дополнительные модули (http://www.opencascade.org/support/products) и техническая поддержка будут платные.
Полноценно сравнить вышеприведенные пакеты, конечно, невозможно без кропотливой работы с каждой из них, однако примерное представление об их функционале можно получить по CAD системам, сделанных на их основе. Исходя из наших финансовых возможностей, у нас выбор был между либо бесплатным OpenCASCADe, либо отечественным C3D. Выбор был сделан в пользу C3D по причине большей стабильности, которая была крайне важна, т.к. требовалось корректно импортировать огромное количество моделей созданных на своем движке. И по первому взгляду на OpenCASCADE сложно предсказать, сколько времени придется повозиться над исправлением его багов. Немаловажным было и наличие импорта/экспорта DXF и стабильных алгоритмов генераций проекций и силуэтов в ядре C3D. Однако, если бы наши задачи были менее специфичны, то, возможно, OpenCASCADE оказался бы более выгодным решением.
В результате мы решились на внедрение ядра от АСКОН, которое заняло у нас немалое время и я хочу поделиться с читателями этим опытом. Ядро C3D представляет набор C++ классов описывающих BREP модели, вспомогательные классы и набор алгоритмов оформленных в виде глобальных функций. На первом этапе внедрения надо было решить, как вообще подключить С++ ядро к программе, которая, по историческим причинам, написана на Delphi. Написание С-интерфейса для всех необходимых функций казалось утомительным и негибким подходом. Выход нашелся довольно быстро – написать промежуточную «интерфейсную» DLL на С++ с использованием чисто абстрактных классов, которые можно напрямую использовать в Delphi, т.к. они содержат внутри себя указатель на VMT, формат которой, к нашему счастью, у компиляторов от MS и Embarcadero полностью совпадает. В результате я написал DLL-прослойку, которая предоставляет необходимый функционал в ООП стиле и экспортирует одну лишь функцию из DLL – получение основного интерфейса.
Следующий этап – построение всех необходимых тел в ядре C3D по исходным данным, их триангуляция и получение триангуляции обратно для визуализации. В этой части не было никаких сложностей. На основе функций построения ExtrusionSolid, EvolutionSolid тела строятся, а с помощью функций BooleanResult они комбинируются друг с другом путем вычитания, сложения и пересечения. На выходе у них образуются тела MbSolid которые и содержат данные BREP – грани MbFace, ребра MbEdge и т.п. Все казалось замечательно, пока не начал тестировать более-менее сложные модели – строить огромное количество объектов без кеширования оказалось не очень быстрым – скорость по сравнению с самописным подходом упала раз в 10, что нас категорически не устраивало. Тут нужно отдать должное ТП у C3D – они помогли переписать функции построения наиболее распространенных тел «вручную», т.е. мы стали строить некоторые тела самостоятельно создавая каждую грань тела и описывая все ребра на них и связывая их друг с другом, получая на выходе корректное замкнутое тело.
Следующий этапом была визуализация – подхватить триангуляцию из C3D и отрисовывать её. Казалось задача проста (учитывая имеющийся рендер на OpenGL), однако обнаружилась неожиданная загвоздка: нам крайне важна визуализация объектов с текстурами, а явной поддержки текстурных координат в ядре C3D нет (видимо потому, что их нет в Компасе 3D). Пришлось выкручиваться следующим образом: ядро позволяет генерировать для треугольников так называемые параметрические координаты и для каждой грани можно подсчитать коэффициенты масштабирования, которые позволят наложить текстуру более-менее реалистично. Однако этот способ не позволяет рассчитать текстурные координаты связано для нескольких граней. Пришлось написать дополнительные «костыли» и с помощью атрибутов связывать координаты текстуры на гранях друг с другом. Кроме того, для визуализации и редактирования моделей нужно передавать различные данные, которые мне также рекомендовали сохранять с помощью атрибутов, представляющие собой массивы (имя-значение), которые можно назначать на тела, грани и рёбра.
Самой сложной частью интеграции стало моделирование тел гнутых фасадов.
Построение подобного тела оказалось очень трудоемкой задачей. Эта задача выявила три самых главных недостатка ядра для наших задач: отсутствие операций деформации, очень нестабильно работают конструктивные операции в телах, содержащих линейчатые поверхности и проблемы с точностью, которые усугублялись низкой точностью самописного ядра, модели от которого необходимо поддерживать и отсутствие возможности управления допусками в ядре C3D.
Завершающим этапом интеграции стали различные операции экспорта/импорта (STEP, DXF), анализа взаимного расположения тел и тому подобное. Отдельное слово хочу сказать об отладке в ходе которой в ядре было выявлено значительное количество ошибок, которые к счастью более-менее оперативно решались разработчиками. Тут я хочу суммировать основные проблемы с которыми мы столкнулись:
1. Недостаточно удобное и структурированное API с множеством легаси-кода, разделение всех типов на 2D/3D и неудобные и неединообразные функции в 2D, cамописные типы и структуры, повторяющие STL, но значительно менее удобные
2. Недокументированные механизмы формирования имен всем элементам построения и необходимость их передавать даже если они не используются
3. Необходимость приводить все данные к точности порядка 1e-6, отсутствие управления точностью и различные ошибки, происходящие при её несоблюдении
4. Отсутствие операций построения пути фрезерования и деформации тел
В целом ядра геометрического моделирования являются очень сложным программным обеспечением и разного рода проблемы при их интеграции являются неизбежными. В нашем случае переход с внутреннего решения на полноценное ядро сэкономило огромное количество времени и решило целый пласт накопившихся проблем и единственное о чем стоит сожалеть нам – так это о том, что не озаботились поиском готовых библиотек изначально. Можно выделить следующие плюсы от перехода на полноценное геометрическое ядро:
1. Экономия значительного объёма времени (потраченного на создание и поддержание собственного решения)
2. Улучшено качество создаваемой геометрии, правильное построение криволинейных поверхностей и границ
3. Получение качественных чертежей с удалением невидимых линий
4. Экспорт/импорт в «сапровские» 3d форматы (STEP, SAT, XT, DXF)
Надеюсь, если кто-то из читателей имеет опыт работы с аналогичными библиотеками, то также поделится опытом их внедрения.