A

AndroidNativeCAD

Темы: cad Android brlcad
+ ещё 3
Native CAD on Android - research conspectus (BRL-CAD / OCCT / Rust kernels), with sources

Нативный CAD на Android: ядра, платформа и оболочка

Аналитический конспект по материалам проектных сессий BRL-CAD (Termux / Android)


Вместо предисловия: о чём этот текст

Этот конспект отвечает на один практический вопрос: возможно ли изготовить нативную систему автоматизированного проектирования (CAD) для Android — то есть приложение, которое устанавливается как обычный APK и работает через штатный интерфейс платформы (Kotlin/Compose), не прибегая ни к X11, ни к proot, ни к обёртке-WebView. Термин «нативный» здесь зафиксирован именно в этом смысле, и он задаёт всю дальнейшую логику.1

Материалом послужили записи рабочих сессий проекта BRL-CAD, развёрнутого в среде Termux на Android. Изложение ведётся по темам, а не по хронологии: сначала разбирается, почему подобная задача вообще упирается в стену; затем сравниваются доступные геометрические ядра; далее рассматривается платформа Android как таковая (графика, политика безопасности); и наконец — то, что уже удалось доказать на практике, и та архитектура, к которой эти доказательства подводят.

Ссылки на первоисточники — URL, пути в дереве BRL-CAD с номерами строк, версии пакетов, выводы команд и записи сессий — вынесены в сноски в конце документа. Сокращения вида S1…S7 обозначают рабочие сессии; их перечень приведён ниже.

Перечень использованных сессий

Обозначение Дата Содержание
S1 2026-10-06 Знакомство с BRL-CAD; перспектива сборки под Termux; GPU-драйверы (Vulkan/Turnip/Zink), программные фолбэки
S2 2026-10-06 Форк и ветка termux, README/TERMUX.md, smoke-тест со STL, TUI против GUI, «три подхода»
S3 2026-10-07 Миграция репозитория, Zulip, qged, как писать GUI для BRL-CAD нативно под Android, GLES/OSMesa/dm-gl, бенчмарки рендера
S4 2026-10-09 Предшествующий опыт: mikehanus/BRLCADeditor
S5 2026-10-10 CadQuery/build123d против BRL-CAD; OCCT против BRL-CAD; Rust-ядра; потолок парадигмы; архитектура оболочки
S6 2026-10-09 misc/termux/brlcad-tui (касательно)
S7 2026-10-06 Взаимодействие агента с живым mged, шаблон контекста (касательно)

1. Постановка задачи и её развитие

Исходная цель была сформулирована как сравнительное исследование: что легче портировать под Android — BRL-CAD или же Python-стек CadQuery/build123d, опирающийся на библиотеку OCCT.2 Однако по мере разбора вопроса исходная формулировка уточнялась и смещалась. Сначала выяснилось, что ключ к ответу лежит не в удобстве языка или тулчейна, а в готовности геометрического ядра к сборке под Android. Затем обнаружилась закономерность: нативный CAD на Android — редкость не случайная, а системная. И, наконец, сложился вывод, определивший всё дальнейшее: краеугольным камнем является само CAD-ядро и его перенос на платформу, а всё остальное — оболочка, интерфейс, средства визуализации — производно от этого.3

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


2. Почему нативный CAD на Android — редкость

2.1 Препятствия разной природы

Принято описывать трудности портирования как «стену», которую нужно взять штурмом. Но точнее говорить об очереди стен, и главная их особенность — разная природа. Сборка и механика — это одна преграда; политика операционной системы и её песочница — совсем другая; объём данных и модель памяти — третья; драйверы и GPU — четвёртая; наконец, вопросы взаимодействия и удобства — это уже не инженерия, а исследование. Именно потому здесь не работает обычная логика «взял одну стену — приблизился к цели»: успех на одном рубеже не даёт никакого рычага на следующем, поскольку рубежи разнородны.4

2.2 Графика стандартизована, геометрия — нет

Контраст с трёхмерными играми, которых на Android великое множество, объясняется не «сложностью» CAD как таковой, а асимметрией стандартизации. Графика на Android стандартизована и обязательна: документ Android Compatibility Definition (CDD) предписывает при наличии Vulkan поддержку профиля Android Baseline 2021, требует GPU-композиции не ниже максимального разрешения дисплея и обязательного профайлинга графического процессора через Perfetto. Про геометрическое ядро в CDD нет ни слова.5

Отсюда и разница жанров. Игра — это пропускная способность, GPU и предвычисленные данные, допускающие плавную деградацию качества. CAD — это задержка, CPU и вычисления «на лету», где корректность бинарна: результат либо верен, либо нет. У игр есть многослойная инфраструктура (Unity, Unreal, SDL, PhysX, FMOD); у CAD подобной «промежуточной прослойки» не существует — разве что арендованные проприетарные ядра.6

2.3 Политика платформы: запрет на исполнение стороннего кода

Отдельную преграду создаёт политика безопасности. Начиная с Android 10 приложение не вправе вызывать execve() для файлов из собственного домашнего каталога и обязано загружать лишь тот код, который встроен в APK. Это одним ударом закрывает целый класс архитектур вида «system() плюс внешние исполняемые файлы», на которых исторически строились многие CAD-инструменты.7

2.4 Исключение, подтверждающее правило

Казалось бы, контраргумент очевиден: Shapr3D — успешный мобильный CAD, значит, задача решаема. Но Shapr3D на Android не существует: его платформы — iPadOS, macOS, Windows и visionOS. Его успех объясняется стечением вполне конкретных условий: арендованное ядро Siemens Parasolid, единая GPU-арена Apple, стилус, поддержанный на уровне операционной системы, сознательно суженный круг задач (прямое моделирование) и венчурное финансирование. И даже при всём этом он остался в стороне от Android.8 Показательно и то, что FreeCAD — проект куда более скромный по ресурсам — также не предпринимал апстрим-усилий в этом направлении: поиск по его трекеру не находит ни одной задачи об Android.9


3. Геометрические ядра: сравнение

3.1 BRL-CAD и Python-стек: расстановка сил

Если понимать «нативный» как «APK плюс штатный интерфейс», ответ становится однозначным: BRL-CAD строго легче. Пользовательский интерфейс в обоих случаях — одна и та же работа на Kotlin/Compose, так что выбор сводится к ядру. И здесь расстановка сил предельно ясна: ядро BRL-CAD уже собрано и работает под aarch64/bionic, тогда как у стека CadQuery/build123d готовность нулевая, ибо его тормозит сборка OCCT (через обвязку OCP) под bionic.10

3.2 Python-стек и его зависимости

Причина столь резкого различия — в распространении готовых бинарных пакетов. Для Python-стека колёс под Android попросту нет: cadquery-ocp (и его вариант без VTK, -novtk) публикуются как manylinux_2_28 и manylinux_2_31 для aarch64, то есть собраны под glibc, а Android живёт на другой реализации стандартной библиотеки — bionic.11 К тому же зависимости стека тяжелы и системны: build123d версии 0.13.0 требует cadquery-ocp-novtk вместе с numpy, scipy, scikit-learn, sympy, ezdxf, fonttools, ipython, anytree, svgpathtools и py-lib3mf, а cadquery 2.8.0 идёт ещё дальше, притягивая trame с trame-vtk (то есть VTK), numba, casadi и nlopt.12 Наконец, штатный редактор cq-editor построен на PyQt5 — а значит, упирается в ту же стену Qt Widgets, что и qged.13

3.3 Python на Android и библиотека Chaquopy

Инструмент Chaquopy, вообще говоря, позволяет запускать Python на Android (CPython версий 3.10–3.14, минимальный уровень API 24). Однако его нативный репозиторий колёс, насчитывающий 133 пакета, не содержит ни ocp, ни OCCT, ни VTK, ни lib3mf, ни casadi, ни nlopt — при том что там есть numpy, scipy, scikit-learn, numba, llvmlite, pandas, matplotlib, shapely, lxml, pillow, opencv-python, torch и tensorflow.14 Иначе говоря, инфраструктура Python-на-Android существует, но ключевого для CAD компонента в ней нет.

3.4 OCCT как ядро

Библиотека OCCT, напротив, официально документирует кросс-сборку под Android: требуются CMake не ниже 3.16 и NDK не ниже r19, уровень API от 21, ANDROID_ABI=arm64-v8a, ANDROID_STL=c++_shared, а командный тестовый харнесс на Tcl (BUILD_MODULE_Draw) следует отключать.15 Важно при этом понимать природу булевых операций OCCT: это толерансные эвристики, а не точная логика. «Допуск совпадения» (tolerance of confusion) зафиксирован равным 1.e-7, а у булевых операций имеется отдельный «fuzzy tolerance», призванный улавливать касания и совпадения.16 Это принципиальный момент: и сторонники, и противники OCCT часто обсуждают её как «точную» библиотеку, тогда как в основе её булевых операций лежат численные допуски.

3.5 Слойная модель: что чему соответствует

Чтобы сравнивать разнородные стеки, полезно выстроить их послойно. Геометрическому ядру librt в мире OCCT соответствует сама OCCT; роль адаптера (прослойки между ядром и языком высокого уровня) играет обвязка OCP, а вовсе не libged, как можно было бы предположить; и, наконец, роль фасада — того слоя, через который с ядром разговаривает пользовательский интерфейс, — в мире BRL-CAD исполняет libged, а в мире Python — CadQuery с build123d. Отсюда прямо следует вывод: Python-стек состоит из трёх слоёв, и наиболее трудный из них — средний, адаптерный (OCP), — и есть настоящая стена. У BRL-CAD адаптерного слоя нет вовсе (ядро говорит на C-ABI напрямую), а фасад уже существует.17

Различие в природе фасадов не менее важно. libged — это командный фасад: текст на входе, текст на выходе, а отрисовка делегируется библиотеке libdm. OCCT же — это библиотека C++ классов, где интерактивное трёхмерное отображение (модуль AIS) является лишь необязательным дополнением. Асимметрия очевидна: libged решает именно контракт «интерфейс — ядро», и текстовые команды идеально ложатся на JNI; OCCT решает задачу просмотра, но контракт (через OCP и pybind11) и оказывается той самой стеной.18

3.6 Rust-ядра

Отдельного рассмотрения заслуживает молодое семейство ядер на Rust. Проверка через GitHub API даёт следующий реальный расклад. Проект fornjot архивирован; в его описании прямо сказано «No longer in development» (2553 звезды). truck — 1586 звёзд, лицензия Apache-2.0, активно развивается, позиционируется как «Rust CAD Kernel». vcad — 431 звезда, Apache-2.0, «BRep CAD kernel in Rust/WASM». OpenGeometry — 510 звёзд, MPL-2.0, «cad kernel for web». Есть и cadcore — 42 звезды, чистый Rust с экспортом в STEP AP203. Наконец, opencascade-rs (267 звёзд) — это не ядро, а биндинги к OCCT.19

По своему жанру truck — полный аналог OCCT: в нём есть truck-geometry (NURBS и B-сплайны), truck-topology (вершины, рёбра, контуры, грани, оболочки и тела), truck-shapeops (булевы операции над телами), truck-meshalgo с truck-polymesh (триангуляция), truck-platform и truck-rendimpl (на базе wgpu/WebGPU) и truck-js (сборка в WASM). Единственное, чего недостаёт в его описании, — поддержки форматов STEP и IGES.20

Привлекательность Rust-семейства для Android лежит, однако, не в зрелости ядер, а в свойствах самого тулчейна: утилита cargo-ndk делает кросс-сборку под Android почти тривиальной, язык гарантирует безопасность работы с памятью (снимая целый класс ошибок C++), а wgpu приходит на смену устаревшему fixed-function OpenGL. Но зрелость этих ядер измеряется годами, тогда как OCCT и BRL-CAD насчитывают тридцать–сорок лет.21

Итог по Rust-ядрам осторожен: это скорее список наблюдения, чем кандидат на немедленное применение. У Rust верный жанр (B-rep), но неверная зрелость; у BRL-CAD неверный жанр (CSG), но верная зрелость; у OCCT и жанр, и зрелость верные — но между ней и Android стоит стена C++ и сборочной обвязки.22


4. Платформа Android на практике: графический стек

4.1 Испытательный стенд

Все измерения, о которых пойдёт речь, выполнены на конкретном устройстве: однокристальная система Snapdragon 8 Elite с графическим процессором Adreno 830, Android 15 (уровень API 36), архитектура arm64-v8a, ro.hardware.egl=adreno. Инструментарий составили clang 21.1.8, CMake 4.4.4, freetype 2.14.3, Mesa 26.2.3 и заголовки с загрузчиком Vulkan версии 1.4.364.23

4.2 Аппаратный рендеринг работает

Вопреки распространённому опасению, аппаратная графика на устройстве доступна — и это подтверждено прямыми измерениями. Драйвер Turnip предоставляет аппаратный Vulkan: deviceName = Adreno (TM) 830, driverName = turnip Mesa driver, версия API 1.4.354 (пакет mesa-vulkan-icd-freedreno 26.2.4).24 Поверх него работает Zink, дающий аппаратный OpenGL/GLES: GL_RENDERER = zink Vulkan 1.4(Adreno (TM) 830 (MESA_TURNIP)), GL_VERSION = OpenGL ES 3.2 Mesa 26.2.3 (требуется EGL_PLATFORM=surfaceless).25 Наконец, clvk предоставляет аппаратный OpenCL 3.0 опять же поверх Turnip. Правда, здесь есть исключение: реализация Rusticl не работает, поскольку требует узла /dev/dri/renderD128, которого на устройстве нет.26

Причина такой доступности — в том, что GPU Adreno 830 открыт через узел /dev/kgsl-3d0 с правами 0666, что и позволяет работать драйверам Mesa на базе kgsl-UAPI. Узлы же /dev/dri/*, напротив, недоступны.27

4.3 Программные фолбэки и их измеренная цена

Там, где аппаратный путь недоступен, остаются программные. Их несколько: lavapipe (единственный программный Vulkan), llvmpipe (программный OpenGL на LLVM 21.1.8), swrast с kms_swrast, а также встроенная в BRL-CAD сборка OSMesa — форк проекта BRL-CAD на основе «классической» Mesa 7.0.4, однопоточный, с поддержкой лишь GL 2.0.28

Насколько велика разница, показывают измерения кадрового времени на реальных моделях BRL-CAD при разрешении 1280×720. Для модели pinewood (86 636 треугольников): OSMesa — 6,34 мс (158 кадров в секунду), llvmpipe — 56,2 мс (17,8), а связка Zink с Turnip — 1,38 мс (726). Для модели shipping_container (55 256 треугольников): OSMesa — 15,88 мс (63 кадра в секунду), llvmpipe — 179,6 мс (5,6), связка Zink с Turnip — 2,72 мс (368).29 Иными словами, аппаратный путь даёт выигрыш в десятки раз уже на сегодняшних, ещё не оптимизированных драйверах.

Особенно важно, что при этом не страдает корректность: Zink с Turnip отрисовывает изображение пиксель в пиксель так же, как OSMesa, — расхождение mean|diff| = 0.00, максимальное расхождение max = 0.30

4.4 Границы и оговорки

Следует, однако, честно назвать и ограничения. Проприетарный OpenCL от Qualcomm блокируется не отсутствием драйвера, а компоновщиком Android: символическая ссылка на библиотеку в системном каталоге больше не срабатывает, поскольку пространство имён разрешает ссылку и отвергает реальный путь в /vendor (обходной путь — LD_LIBRARY_PATH=/vendor/lib64). Но даже при успешной загрузке Qualcomm-реализации ICD не инициализируется: failed to get extension function address clIcdGetPlatformIDsKHR, а библиотека /vendor/lib64/libdcap.so отсутствует.31

Не следует забывать и о том, что Turnip, Zink и clvk — драйверы, полученные обратной разработкой. Возможны артефакты и падения, производительность ниже, чем у нативного драйвера Adreno, и повышенный нагрев. Нативный gallium-драйвер freedreno недоступен, поскольку требует интерфейса msm-DRM, тогда как в наличии лишь kgsl; virgl же работает исключительно под QEMU.32


5. Пользовательский интерфейс: почему qged не годится

5.1 Qt Widgets как тупик

Готовый интерфейс BRL-CAD, qged, — это приложение на Qt Widgets объёмом порядка девятнадцати тысяч строк (src/qged — 165 файлов, из них 34 на C/C++, 6999 строк; src/libqtcad — 11 999 строк). Android-стек Qt — это Quick/QML, а Widgets платформой не поддерживается — значит, в APK такой интерфейс не поставляется.33

Более того, qged уже работает на ARM-Android — но только как bionic-бинарь под Termux:X11, а не как APK. Новый интерфейс mged -C при этом падает из-за несовместимости версии Itcl (have 4.3.2, need exactly 3.4), и потому не рекомендуется к использованию.34 Показательно, что и сам апстрим считает панели qged недоделанными: по свидетельству из Zulip, по меньшей мере одна из панелей управления видом нуждается в переработке.35

5.2 Корень проблемы отрисовки

Настоящая причина стены — в архитектуре рендеринга. Общий модуль src/libdm/dm-gl.c интенсивно использует fixed-function и immediate-mode OpenGL, которого в GLES просто нет: подсчёт показывает 16 вызовов glBegin, 16 — glMatrixMode, 19 — glVertex, 4 — glOrtho, 26 — glEnable, 26 — glLight. Готового EGL-драйвера в дереве нет: доступны только X, glx, qtgl, wgl, swrast, tkswrast, plot, postscript и txt.36

5.3 Возможные варианты

Из этой ситуации было намечено три варианта интерфейса: (A) Kotlin/Compose в связке с JNI; (B) Qt Quick/QML; (C) SDL2 в сочетании с Dear ImGui или Nuklear на C++. Предпочтение отдано варианту А.37 Параллельно были намечены и три маршрута отрисовки: (R1) программный рендеринг во внеэкранный буфер через dm-swrast/OSMesa с последующим копированием; (R2) перенос fixed-function dm-gl.c в новый драйвер dm-gles на базе GLES3; (R3) внеэкранная трассировка лучей (rt → icv_write_mem → растровое изображение).38 Общий недостающий компонент для аппаратной отрисовки, таким образом, один: новый display-manager на EGL (dm-egl); в src/libdm EGL и Zink фигурируют пока лишь как заготовки // TODO.39

5.4 Точки сопряжения для JNI

Для сопряжения с ядром через JNI пригодны несколько устойчивых точек входа: ged_create (include/ged/defines.h:296), ged_exec (include/ged/commands.h:42) и db_open (include/rt/db_instance.h:168), а независимый от инструментария интерфейс представления даёт bview (include/bv/defines.h:573).40 Существенно, что libged написан на чистом C и не зависит от Tcl: привязка к Tcl живёт отдельно, в libtclcad, а значит, командный движок можно переиспользовать через JNI напрямую.41

Здесь, однако, требуется важная оговорка. Исходники libged действительно свободны от Tcl, но сборка по умолчанию всё равно линкует Tcl: опция BRLCAD_ENABLE_TCL включена по умолчанию, из-за чего в списке зависимостей libged.so.20 присутствует libtcl8.6.so. Сборка без Tcl при этом штатно поддержана — соответствующий режим no_tcl существует в системе сборки. Иначе говоря, Tcl-зависимость libged — артефакт конфигурации, а не свойство кода.42

5.5 Подводные камни интеграции

Практическая интеграция ядра в Android-приложение таит несколько ловушек. Движок не потокобезопасен, поэтому необходим один поток движка и очередь команд; цикл обработки событий следует заменить на ged_create_io_handler/ged_fbs; структура db_i держит файл открытым, чем нужно управлять вручную; наконец, URI схемы content:// из Storage Access Framework несовместимы с путевым API db_open, а X11, Tcl и Tk попросту отсутствуют.43

Стоит добавить, что в BRL-CAD нет и самой инфраструктуры сборки под Android: ни androiddeployqt, ни ANDROID_ABI, ни Q_OS_ANDROID; макрос __ANDROID__ встречается исключительно в вендоренном коде.44


6. Что уже удалось доказать на Android

6.1 Сборка и запуск

Ядро BRL-CAD собрано и работает на Android/aarch64. Проверка велась на версии BRL-CAD 7.42.1 @48a87e7e13, позднее подтверждённой на 7.46.x @7929a747ad, при помощи clang 21.1.8 с libc++ (bionic), CMake 4.4.4 и цели --target=aarch64-linux-android36; сборка в режиме Release посредством make -j6 достигла ста процентов.45

По пути пришлось устранить три регрессии линковки, внесённые в апстриме 7.46.x и ломавшие сборку на bionic: библиотека libbu вынесла M_LIBRARY из публичных BU_LIBS (что давало undefined symbol: modf); модуль поиска OpenNURBS не линковал FreeType для статического архива; а tinygltf перешёл на API третьей версии, тогда как в окружении был закреплён второй.46

Кроме того, потребовались локальные исправления. В src/libbu/semaphore.c пришлось выровнять структуру bu_semaphores по восьми байтам: дело в том, что в bionic тип pthread_mutex_t выровнен лишь по четырём байтам, из-за чего возникало ложное сообщение о нарушении выравнивания, приводившее к вызову bu_bomb() и рекурсивной блокировке на futex. Пришлось также подключить libandroid-shmem и отключить в regress/CMakeLists.txt проверку fuzz, поскольку рантайм санитайзеров на платформе отсутствовал (undefined symbol: __sancov_lowest_stack).47

6.2 Артефакты ветки termux

Работа материализовалась в форке github.com/chainreaction/brlcad, в ветке termux (форк-хед после работ над TUI — b2f3aa7fe9). В каталоге misc/termux/ собраны AGENT.md, bext-extra-edits.sh, brlcad-tui, smoke-test.sh, start-work.sh, tmux.conf и набор патчей для внешних зависимостей (основной патч BRL-CAD, патч драйверов bext, а также патчи для geogram, OpenNURBS, PoissonRecon, stepcode и Utah RLE).48 Результаты были опубликованы: обсуждение в апстриме BRL-CAD («BRL-CAD 7.46.x on Termux (Android/aarch64) — working port + build guide»), задача в собственном трекере, сообщение в Zulip и руководство TERMUX.md.49

6.3 Сквозной smoke-тест

Главное подтверждение — сквозной headless-цикл: создание примитивов, булевы операции CSG, анализ объёма и экспорт в STL. На модели demo.r = box - ball параллелепипед ARB8 размером 1000 мм дал объём 1 000 000 000,0 мм³, а сфера радиуса 500 — 523 598 775,6 мм³. Экспорт прошёл успешно: текстовый demo.stl занял 66 264 байта и содержал 300 треугольников, бинарный demo_bin.stl — 15 084 байта при тех же 300 треугольниках (проверка сходится: 84 + 300×50 = 15 084).50

Из этого теста остался один незакрытый вопрос: обратный импорт STL, то есть круговой маршрут g-stl → stl-g.51


7. Интерактивность и потолок парадигмы

7.1 Четыре уровня интерактивности

Интерактивность удобно разложить на четыре уровня, и это разложение сразу показывает границу возможного. Первый уровень — навигация (орбита, масштабирование, панорамирование, вписывание) — достижим, поскольку относится к рендерингу, а не к ядру. Второй — живое превью при правке CSG-модели — достижим с оговорками, ибо требует пересчёта дерева, а булевы операции на больших деревьях становятся узким местом. Третий — эскиз с ограничениями, как в FreeCAD, — недостижим по причине отсутствия решателя ограничений. Четвёртый — прямое моделирование «тяни-толкай», как в Shapr3D, — недостижим из-за отсутствия слоя прямого редактирования B-rep.52

7.2 Потолок задаётся ядром

Отсюда — трезвый вывод о конечной цели: это современный по форме мобильный CSG-моделлер уровня Tinkercad, OpenSCAD или CadQuery, но никак не клон Shapr3D, Fusion или FreeCAD. Потолок задан именно ядром, а не оболочкой.53

В этом ограничении, однако, есть и сильная сторона. CSG оперирует малыми данными — деревом, а не графом B-rep, — а трассировка лучей хорошо распараллеливается и даёт точную картину без артефактов триангуляции. Этого как раз лишены экранные представления FreeCAD и Shapr3D. Для телефона CSG оказывается удобнее, чем OCCT с её гигабайтами B-rep-данных.54


8. Архитектура оболочки

8.1 Команды как канонический интерфейс

Ключевое архитектурное решение вытекает из природы libged: функции ged_exec(gedp, argc, argv) отводится роль канонического интерфейса и, по существу, единственного источника истины, а любой пользовательский интерфейс превращается в генератор командных строк. Приятный побочный эффект — автодополнение, уже готовое в ged_cmd_list() и ged_cmd_completions().55 Поток исполнения при этом выстраивается так: интерфейс Compose (главный поток) → потокобезопасная очередь → единственный поток движка → ged_exec → результат в ged_result_str и журнал в ged_log вместе с рендерингом → обратный вызов → перерисовка. Ядро при этом никогда не затрагивается из нескольких потоков.56

8.2 Фасад, интенты и запас на смену ядра

Рано или поздно встанет вопрос о смене ядра. Здесь важно осознать, что шов проходит по фасаду, а не по грамматике: командная грамматика CSG (put, comb, attr) привязана к парадигме и на B-rep-ядро вроде truck не переносится. Отсюда два практических правила: во-первых, все вызовы движка должны быть сосредоточены в одном модуле-фасаде; во-вторых, очередь должна нести интенты оболочки, а не строки ged. Набор таких интентов невелик — открыть файл и получить дерево, узнать свойства объекта, задать параметр, выполнить булеву операцию, отрисовать вид, выполнить выбор по координате, экспортировать результат.57

Полезно заранее понять, что переживёт переход на Rust-ядро, а что нет. Полностью сохранятся вьюпорт с рендерингом и выбором, очередь с потоками и граница JNI, а также каркас Compose — то есть все «трубы». А вот дерево объектов и панели параметров придётся пересобирать: дерево CSG-примитивов и дерево конструктивных элементов — не одно и то же.58 Отсюда и порядок работ: сначала дорогое и переиспользуемое (рендеринг, вьюпорт, выбор), затем дешёвое и сменное (дерево и панели).59

8.3 Промежуточный этап: CLI/TUI

Наконец, стоит упомянуть «нулевой» этап работы, реализованный ещё до всякого интерфейса. «Три TUI-подхода» на поверку оказались не тремя альтернативами, а двумя слоями и клиентом: tmux как обязательная подложка, mged -c/-p как доменный инструмент и агент как ускоритель. Соответствующие артефакты — misc/termux/tmux.conf, start-work.sh и установщик brlcad-tui --install — уже существуют.60


9. Предшествующий опыт: mikehanus/BRLCADeditor

Отдельного разбора заслуживает единственный найденный предшественник — mikehanus/BRLCADeditor. Это приложение на Qt5 Widgets для настольных систем, написанное на C++ под лицензией BSD-3-Clause в рамках Google Code-In 2017 и заброшенное 23 января 2018 года; его объём — 32 КБ, звёзд нет. Ни Android, ни NDK, ни JNI, ни Java в дереве проекта не найти.61

Архитектурно он устроен как оркестрация внешних CLI-бинарников через system() — вызываются g2asc, asc2g, rt, pix-png, dxf-g и stl-g — к чему добавлена компиляция пользовательского C++-«скрипта» на лету посредством g++.62 Понятно, что в APK такая схема не переносится: на устройстве нет ни /usr/brlcad/bin, ни компилятора, а каждый перезапуск оборачивается порождением процесса и дисковым обменом. Qt Widgets в роли Android-интерфейса, как уже говорилось, тоже не годится.63

Тем не менее кое-что из этого опыта полезно. Во-первых, он независимо подтверждает тот самый минимальный продукт «внеэкранный rt → растровое изображение»: вся его «интерактивность» сводится к передвижению ползунка, после чего трассировка запускается заново. Во-вторых, ценна сама идея текстовой модели как источника истины. В-третьих, файл libs/primitives.h служит компактным образцом того, как дружелюбный программный интерфейс транслируется в команды mged. Выписанный из этого синтаксис (вне рабочего дерева проекта) может пригодиться впоследствии.64

Здесь же уместно предостеречь от одной ловушки — существования двух несовместимых форматов .asc. Устаревший формат COMGEOM хранит точки параллелепипеда arb8 относительно первой вершины, тогда как формат v5 использует абсолютные координаты. Смешивать их нельзя.65


10. Вместо заключения: открытые вопросы

Разбор приводит к нескольким вопросам, которые ещё предстоит закрыть:

  • Обратный импорт STL — проверить круговой маршрут g-stl → stl-g, оставшийся от сквозного теста (S2).
  • Драйвер dm-egl и GLES3 — главный недостающий компонент аппаратной отрисовки (S3).
  • Вторая «дверь» JNI — открыть .g и выполнить who; это ближайший практический шаг.
  • Апстрим генерик-исправлений bext и упаковка под Termux (S2).
  • Пилот фасада-интентов (открыть, дерево, задать параметр, отрисовать) на BRL-CAD — чтобы на практике оценить запас прочности при смене ядра (S5).

Общий же вывод таков. Если понимать «нативный» как «APK плюс штатный интерфейс», то BRL-CAD оказывается единственным ядром, которое уже сегодня доказало свою работоспособность на Android; его потолок — CSG-парадигма уровня Tinkercad и OpenSCAD, но её трубы (вьюпорт, рендеринг, очередь, граница JNI) независимы от ядра и переживут любую будущую его замену.


Примечания (первоисточники)

  1. Сессия S5. Определение «нативно» дано пользователем: «нативно это установка через apk и ui штатный для android приложений». ↩

  2. Сессия S5. ↩

  3. Сессия S5 (цепочка вопросов пользователя: «почему попытки собрать cad на android всегда натыкаются на очередь высоких стен»; «краеугольный камень… cad ядро → порт android»). ↩

  4. Сессия S5 (ответ на вопрос об «очереди высоких стен»). ↩

  5. Сессия S5; Android CDD 14, пункты [7.1.4.2/H-1-1], [7.1.1.1/H-0-2], [7.1.4.6/H-0-1…H-1-4] (https://source.android.com/docs/compatibility/14/android-14-cdd). ↩

  6. Сессия S5. ↩

  7. Сессия S5; https://developer.android.com/about/versions/10/behavior-changes-10 («Untrusted apps that target Android 10 cannot invoke execve() directly on files within the app's home directory»; «Apps should load only the binary code that's embedded within an app's APK file»). ↩

  8. Сессия S5; https://www.shapr3d.com (разделы download/pricing/home): ни одного упоминания «Android»; на сайте — «built with the Siemens Parasolid® kernel under the hood»; iPadOS 17+ «preferably with Apple Pencil». ↩

  9. Сессия S5; поиск по GitHub API («Android in:title repo:FreeCAD/FreeCAD») — total 0. ↩

  10. Сессия S5; сборка BRL-CAD под Termux подтверждена в сессии S2 (make -j6 достигает 100 %, тест mged и rt проходит). ↩

  11. Сессия S5; PyPI (https://pypi.org/pypi/cadquery-ocp/json): файл cadquery_ocp_novtk-8.0.1.1.0-cp312-manylinux_2_28_aarch64.whl (58,7 МБ). ↩

  12. Сессия S5; PyPI JSON для build123d и cadquery. ↩

  13. Сессия S5; PyPI, пакет cq-editor. ↩

  14. Сессия S5; https://chaquo.com/pypi-13.1/ (репозиторий пакетов Chaquopy). ↩

  15. Сессия S5; dox/build/build_occt/building_occt.md, раздел «Cross-compiling (Android)». ↩

  16. Сессия S5; src/FoundationClasses/TKernel/Precision/Precision.hxx:165 (static constexpr double Confusion() { return 1.e-7; }); src/ModelingAlgorithms/TKBO/BOPAlgo/BOPAlgo_Options.hxx:123 (void SetFuzzyValue(const double theFuzz)). ↩

  17. Сессия S5 (ответы на вопросы «если мы соберём OCCT под termux?» и «ocp идеологически аналог libged?»). ↩

  18. Сессия S5 (ответ на вопрос «libged строился как текстовый командный интерфейс, occt строился под интерактивное 3d?»). ↩

  19. Сессия S5; GitHub API (api.github.com/repos/…) и поиск api.github.com/search/repositories?q=cad+kernel+language:rust. ↩

  20. Сессия S5; https://raw.githubusercontent.com/ricosjp/truck/master/README.md. ↩

  21. Сессия S5; зрелость BRL-CAD и OCCT отражена также в сессиях S3 и S5. ↩

  22. Сессия S5. ↩

  23. Сессия S1; команды uname, getprop ro.product.cpu.abi, getprop ro.hardware.egl, pkg list-installed. ↩

  24. Сессия S1; vulkaninfo --summary. ↩

  25. Сессия S1; EGL_PLATFORM=surfaceless GALLIUM_DRIVER=zink ./egltest. ↩

  26. Сессия S1; clinfo с OCL_ICD_VENDORS=…; пакет clvk 0.0.20260707.165306. ↩

  27. Сессия S1; также сессия S3 («Turnip opens the GPU node directly»). ↩

  28. Сессия S1 (vulkaninfo, /lib/dri/…); сессия S3 (src/libdm/swrast/CMakeLists.txt → brlcad_find_package(OSMESA REQUIRED), libosmesa.so = 2 823 504 байта). ↩

  29. Сессия S3 (бенчмарк в транскрипте сессии). ↩

  30. Сессия S3. ↩

  31. Сессия S1; вывод компоновщика (is not accessible for the namespace), KHR ICD trace at …/icd.c:123, readelf: Error: '/vendor/lib64/libdcap.so': No such file. ↩

  32. Сессия S1. ↩

  33. Сессия S3; src/qged/CMakeLists.txt (target_link_libraries(qged libqtcad libged librt libbu Qt6::Widgets Qt6::Network)). ↩

  34. Сессия S3 (запуск под X11); сессия S2 (падение mged -C). ↩

  35. Сессия S3; Zulip. ↩

  36. Сессия S3; подсчёт вызовов по src/libdm/dm-gl.c; перечень драйверов src/libdm. ↩

  37. Сессия S3 (обсуждение архитектуры); частично не подтверждено — мнение. ↩

  38. Сессия S3; для маршрута R3 — рабочий конвейер из сессий S7 и S4 (rt -e … -o file.pix, затем pix-png); частично не подтверждено — мнение. ↩

  39. Сессия S3. ↩

  40. Сессия S3. ↩

  41. Сессия S3; src/libged/CMakeLists.txt. ↩

  42. Сессия S5. Оговорка: исходники libged свободны от Tcl, но сборка по умолчанию линкует Tcl (BRLCAD_ENABLE_TCL включена по умолчанию, misc/CMake/BRLCAD_User_Options.cmake:216; DT_NEEDED libtcl8.6.so в libged.so.20). Сборка без Tcl штатно поддержана: create_distcheck(no_tcl … -DBRLCAD_ENABLE_TCL=OFF …) в misc/CMake/Distcheck.cmake:210. ↩

  43. Сессия S3 (обсуждение; где не подкреплено кодом — мнение). ↩

  44. Сессия S3. ↩

  45. Сессия S2. ↩

  46. Сессия S2; сообщение коммита «Termux: fix new-base link regressions…». ↩

  47. Сессия S2; сессия S1 (ошибка fuzzer'а). ↩

  48. Сессия S2. ↩

  49. Сессия S2. ↩

  50. Сессия S2; команды mged -c (make …, r demo.r u box - ball, analyze demo.r), rt -s 64 -p 0 -o v2.pix v2.g sph, g-stl -o demo.stl demo.g demo.r. ↩

  51. Сессия S2. ↩

  52. Сессия S5; наличие выбора (picking) подтверждается модулями qray, polyclip и track в libged (сессия S5, link.txt). ↩

  53. Сессия S5. ↩

  54. Сессия S5. ↩

  55. Сессия S5; API — сессия S3 (см. §5.4). ↩

  56. Сессия S5; дисциплина потоков — сессия S3. ↩

  57. Сессия S5. ↩

  58. Сессия S5. ↩

  59. Сессия S5. ↩

  60. Сессия S2 (mged -p — режим конвейера с маркерами CMD_DONE; termux-wake-lock). ↩

  61. Сессия S4; GitHub API (api.github.com/repos/mikehanus/BRLCADeditor). ↩

  62. Сессия S4; поиск по editor.cpp. ↩

  63. Сессия S4. ↩

  64. Сессия S4. Синтаксис DSL выписан вне рабочего дерева проекта (~/tmp/brlcad-notes/brlcadeditor-dsl.md). ↩

  65. Сессия S4: устаревший COMGEOM (src/conv/asc/asc2g.c, solbld(), VADD2(pnts[i], pnts[i], pnts[0])) хранит точки относительно V1; формат v5 (src/libgcv/plugins/asc/asc_v5.cpp) — абсолютные. ↩