Генерация 3д объекта - как правило, многоэтапный процесс (например в булевых операциях сначала поиск графа пересечений, нахождение геометрии кривых пересечения и построение топологии результирующего тела). Закономерно возникает сложность с его отладкой. Положим при генерации что-то пошло не так и имеем наполовину готовый объект, который не может быть визуализирован разрабатываемой CAD системой. Что делать? Как локализовать место и момент ошибки? Анализировать глазами тысячи xyz координат промежуточных результатов и вспомогательных объектов на момент выдачи исключения? Или хуже, если отклонения желаемого результата от фактического незначительные, тогда и все числа внешне будут корректны. Работая С++ программистом в области 3Д моделирования и построения различных CAD/САПР систем, я регулярно сталкивался с проблемой визуализации вспомогательных/промежуточных сущностей.
Сформировал себе универсальный инструментарий DumpSTL, позволяющий с минимальными усилиями, в любом C++ проекте дампить в .stl файлы любые внутренние объекты в проекте.
Почему именно .stl? Так уж исторически сложилось. Много использовал чпу фрезера и 3д принтера, где основным и простейшим форматом моделей является .stl.
Суть использования сводится к однократной адаптации инструмента под структуры данных конкретного проекта, затем:
1) подключить один DumpSTL.h;
2) вызвать к необходимым данным метод DUMP::save(...)
;
3) получить на выходе множество файлов с 3д моделями, которые можно открыть в любом 3д редакторе.
Примеры использования
Пример 1: По ходу генерации 3д модели музыкального инструмента - флейты Пана
используются аналитические функции для проецирования точек по определенным правилам, rоторые могут быть визуализированы поверхностями для лучшего понимания областей определения и итоговой геометрии.
Пример 2: Визуализация формы математических функций – сплайнов, по которым натягиваются некая внутренняя поверхность и формируется геометрия входного отверстия (это не просто круг и цилиндр, постарался показать синим и красным гипертрофированный изгиб).
Получаемые при этом графики функций сплайнов в плоскости XY:
Пример 3: Обозначение ориентации кривых в пространстве, что бы понять где начало, а где конец.
Адаптация под проект / геометрическое ядро
Покажу на примере библиотеки MeshLib.
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. Может быть полезно при полуавтоматическом прогоне тестов и относительно быстрой проверки глазами, что ничего не попортилось путем быстрого пролистывания скриншотов.
Комментарии (7)
Xadok
17.08.2022 20:55+1Интересно что подобное решение есть для буста, только в ином виде github.com/awulkiew/graphical-debugging
Chuvi
18.08.2022 08:55+1Спасибо, как раз искал что-то подобное.
С вашего позволения, чуть попридираюсь к коду
Использование
std::string_view без соответствующего #include. У вас, видимо, string_view заинклудился каким-то из имеюшихся инклудов. На других платформах такого может не случиться.
У пользователя может не быть директории "C:\Repos\STL\", как и диска "C:\". Вдруг у него не windows? Значит пользователю придётся лезть в код библиотеки и править этот путь, что нехорошо. (Может есть смысл сделать эту переменную не constexpr?)
Почему 21? Почему 84? Что это за числа? Подозреваю, что здесь имелось ввиду
file.write((char*)dummy, sizeof(dummy));
-
Там же дальше
file.write((char*)&(triangles[0]), static_cast<std::streamsize(triangles.size()) * 50);
50 это что? sizeof(Triangle)?
ArraLazur Автор
18.08.2022 11:10+12) Пользователю данного инструмента требуется указать свою папку, где он хочет получать дампы + еще и написать набор convert методов. Т.е. предполагается, что в любом случае придется лезть и дописывать код библиотеки под себя и под свои проекты
3,4) https://en.wikipedia.org/wiki/STL_(file_format) 80+4 байта заголовок, 50 байт каждый треугольник, 21 int просто что бы добрать первые 84 байта. Согласен, числа магические, но по сути определяются форматом и не изменяемые и для пользователя значения не имеютChuvi
18.08.2022 12:19+3Пользователь не должен редактировать код библиотеки. По-хорошему, у него должна быть возможность написать дополнительные функции для библиотеки у себя в проекте. Предположим, ваш проект будет жить и развиваться, вы и другие пользователи будут вносить в него дополнения, исправлять ошибки, и так далее. И при каждом обновлении пользователь будет вынужден исправлять код библиотеки "под себя".
В целом, после некоторого колдовства я собрал её под linux. Кстати, file.open не должен принимать string_view в качестве аргумента.
Ловите пулл-реквест, я там заодно поддержку cmake добавил. https://github.com/KupchishinAB/DumpSTL/pull/3
starfair
Хорошая идея! Жаль нет кармы, плюс вам поставить за материал!
А скажите, не встречался ли вам по пути, какой нибудь достаточно легкий по объёму инструмент (библиотека или SDK) на С++ который бы поддерживал 2 для меня крайне важных момента:
1) кривые как сочетание прямых отрезков так и сплайнов;
2) основные булевые операции над пересечениями таких кривых с генерацией новых или коррекцией исходных
Всё, что я ни находил, либо жутко громозкое, либо работает только с полилиниями состоящими из прямых отрезков (или с большим набором мелких спрямленных сегментов вместо исходной кривой секции). А хотелось бы что-то , что позволяло бы делать базовые операции в легкой 2D CAD системе без сильного усложнения кода.
ArraLazur Автор
Спасибо за отзыв, к сожалению мне таких тоже не известно, да и не искал, не сталкивался с такими требованиями
starfair
Ну, в любом случае - спасибо вам за статью и конечно за библиотеку! Надуюсь, дойдут и у меня руки до создания того, что запланировал.