Отладка в C++ геометрии и топологии

Генерация 3д объекта - как правило, многоэтапный процесс (например в булевых операциях сначала поиск графа пересечений, нахождение геометрии кривых пересечения и построение топологии результирующего тела). Закономерно возникает сложность с его отладкой. Положим при генерации что-то пошло не так и имеем наполовину готовый объект, который не может быть визуализирован разрабатываемой CAD системой. Что делать? Как локализовать место и момент ошибки? Анализировать глазами тысячи xyz координат промежуточных результатов и вспомогательных объектов на момент выдачи исключения? Или хуже, если отклонения желаемого результата от фактического незначительные, тогда и все числа внешне будут корректны. Работая С++ программистом в области 3Д моделирования и построения различных CAD/САПР систем, я регулярно сталкивался с проблемой визуализации вспомогательных/промежуточных сущностей.               

Сформировал себе универсальный инструментарий DumpSTL, позволяющий с минимальными усилиями, в любом C++ проекте дампить в .stl файлы любые внутренние объекты в проекте.
Почему именно .stl? Так уж исторически сложилось. Много использовал чпу фрезера и 3д принтера, где основным и простейшим форматом моделей является .stl.

Суть использования сводится к однократной адаптации инструмента под структуры данных конкретного проекта, затем:
1) подключить один DumpSTL.h
2) вызвать к необходимым данным метод DUMP::save(...)
3) получить на выходе множество файлов с 3д моделями, которые можно открыть в любом 3д редакторе

Примеры использования:
Пример 1: По ходу генерации 3д модели музыкального инструмента - флейты Пана  

3д модель панфлейты
3д модель панфлейты

используются аналитические функции для проецирования точек по определенным правилам, rоторые могут быть визуализированы поверхностями для лучшего понимания областей определения и итоговой геометрии.

Визуализация вспомогательных поверхностей
Визуализация вспомогательных поверхностей

Пример 2: Визуализация формы математических функций – сплайнов, по которым натягиваются некая внутренняя поверхность и формируется геометрия входного отверстия (это не просто круг и цилиндр, постарался показать синим и красным гипертрофированный изгиб)

Использование сплайновых кривых
Использование сплайновых кривых

Получаемые при этом графики функций сплайнов в плоскости XY:

Кривые - сплайны в плоскости XY
Кривые - сплайны в плоскости XY

Пример 3: Обозначение ориентации кривых в пространстве, что бы понять где начало, а где конец.

Отображение ориентации UV поверхности
Отображение ориентации UV поверхности

Адаптация под проект / геометрчиеское ядро:
Покажу на примере библиотеки MeshInspector

1) Указать путь к папке, куда будут дампиться .stl файлы в глобальной переменной.std::string_view folderSTL = "C:\\Repos\\STL\\"

2) Подключить include в DumpSTL.h с необходимыми структурами, в данном примере это положим MR::Mesh (полная сетка 3д тело), MR::FaceBitSet (подмножество треугольников сетки).

3) Написать функции преобразования в DUMP::Model3D для каждого такого пользовательского типа (аргумент может быть не один):
    а) Model3D convert(const MR::Mesh&, const MR::FaceBitSet& );
    б) Model3D convert(const MR::Mesh& );

4) По необходимости написать преобразования пользовательских типов в множество точек const std::vector<Point3f>& points, которые могут быть использованы для дампа с особыми модификациями:
    a) Model3D direction(const std::vector<Point3f>& points) – порождает по упорядоченному множеству точек ориентированную цепочку конусов (пирамидок);
    б) Model3D line(const std::vector<Point3f>& points) – порождает по упорядоченному множеству точек отрезки;
    в) Model3D sphere(const std::vector<Point3f>& points) – порождает по множеству точек низкополигональные сферы.

Пример реализации convert методов

DUMP::Point3f convert( const MR::Vector3f& point )
{
    return { point.x, point.y, point.z };
}

DUMP::Model3D convert( const MR::Mesh& mesh, const MR::FaceBitSet& faces)
{
    DUMP::Model3D res;
    for ( auto f : faces )
    {
        MR::Vector3f a, b, c;
        if ( mesh.topology.hasFace( f ) )
        {
            mesh.getTriPoints( f, a, b, c );
            res.addTriangle( convert(a), convert(b), convert(c));
        }
    }
    return res;
}

DUMP::Model3D convert( const MR::Mesh& mesh )
{
    return convert( mesh, mesh.topology.getValidFaces() );
}

Как пользоваться
Применять методы ниже к стуктурам данным проекта и получать генерируемые 3д модели:

1) void save(std::string_view fileName, Args... args) – сохраняем один файл, где fileName– только название файла (нужная папка в folderSTL), args– передаем напрямую нужные нам объектыsave(“mesh”, my_mesh), save(“mesh_part”, my_mesh, my_faceBitSet)

2) void saveInc(std::string_view fileName, Args... args) – то же самое что и save, с той лишь разницей, что сохраняет всегда в новый файл. Если файл с указанным именем существует, приписывает цифру-счетчик еще не существующего файла. Так например если процесс итерационный, и на 135 итерации все взрывается, то есть возможность увидеть эволюцию 3д объекта во всех 135 итерациях, и увидеть, что было непосредственно перед падением приложения.

3) Использовать модификаторы direction/line/sphere: save(“mesh”, direction(mesh)), save(“mesh_part”, direction(my_mesh, my_faceBitSet))

4) Создовать напрямую объекты Model3D, используя напрямую его API addTriangle/addPoint/addEdge/addCone/addQuad/addSphere и дампить его так же save(“model3D”, my_Model3D)

Hello world
Пример использования и соответственно запускаемый "hello world" можно найти в соседнем проекте в репозитории example. При запуске из коробки должны получаться следующие примитивы:

Кубик, тетраэдр, немного конусов, сферы...
Кубик, тетраэдр, немного конусов, сферы...

И вот такой набор файлов с 3д моделями: dumpStlExample_cube.stl, dumpStlExample_directionChain_0.stl, dumpStlExample_directionChain_1.stl, dumpStlExample_directionChain_2.stl, dumpStlExample_lineChain.stl, dumpStlExample_points.stl, dumpStlExample_spheres.stl, dumpStlExample_tetraedr.stl

Как устроена библиотека:

1) Представляет из себя один подключаемый header only DumpSTL.h репозиторий
    а) В нем описаны тривиальные структуры Point3, Triangle, Model3D с сопутствующими методами
    б) Функции для пользователя save, saveInc (incremental)
    в) Модификаторы: direction, sphere, line

2) Проект под студию с примером использования и известным ожидаемым результатом example.sln

3) Скрипт clear_stl.bat для автоматической чистки папки от накопившихся .stl файлов (их могут быть сотни)

4) Скрипт run_blender.bat + blenderScrypt.py для автоматического открытия в 3д редакторе blender всех файлов в папке по маске *.stl и фотографирования каждого с 4ех ракурсов и сохранением фото в эту же папку с припиской _left.jpg, _right.jpg, _top.jpg, _bottom.jpg. Может быть полезно при полуавтоматическом прогоне тестов и относительно быстрой проверки глазами, что ничего не попортилось путем быстрого пролистывания скриншотов.