Чем ZenCad 2 отличается от ZenCad 1
ZenCad 2 сохраняет скриптовый способ моделирования на Python, но меняет установку геометрического backend, устройство визуализатора и представление результатов вычислений. Эти изменения упрощают установку, отделяют вычисление модели от её отображения и делают API более предсказуемым для человека и программных инструментов.
Геометрическое ядро и установка из PyPI
Само геометрическое ядро остаётся OpenCascade. Меняется способ доступа к нему из Python: вместо pythonocc-core используются привязки cadquery-ocp-novtk с модулями OCP.
В ZenCad 1 установка геометрических зависимостей требовала отдельного внимания к поставке pythonocc-core и OpenCascade. В ZenCad 2 backend поставляется готовыми бинарными wheel через PyPI и устанавливается как зависимость ZenCad. Для поддерживаемых платформ не нужно отдельно собирать OpenCascade или создавать окружение Conda.
python3 -m pip install "zencad[gui]"
Это команда установки опубликованного пакета. Если нужная ревизия ZenCad 2 ещё не опубликована, используйте установку из исходников. Варианты установки и требования к платформе описаны в разделе «Установка».
Постоянный viewer вместо встраиваемых окон
В ZenCad 1 процесс модели создавал собственное окно визуализации, которое встраивалось в главное окно приложения. Пересчёт модели затрагивал не только вычисления, но и жизненный цикл этого окна. Такая схема зависела от механизмов оконной системы.
В редакторе ZenCad 2 viewer принадлежит главному GUI-процессу и сохраняется между запусками скрипта. В этом процессе находятся Qt, OpenGL, камера и объекты отображения OpenCascade. Отдельный исполнитель модели занимается геометрией и не создаёт окно, которое нужно встраивать.
| ZenCad 1 | ZenCad 2 |
|---|---|
| Процесс модели создаёт окно viewer. | Процесс модели собирает данные сцены. |
| Главное окно встраивает окно другого процесса. | Главное окно отображает данные в собственном постоянном viewer. |
| Перезапуск вычислений связан с заменой окна модели. | Перезапускается исполнитель; viewer и камера сохраняются. |
При прямом запуске python model.py вызов show() открывает самостоятельный viewer в процессе скрипта. Описанное разделение GUI и исполнителя относится к работе редактора.
Как геометрия передаётся между процессами
Исполнитель собирает описание сцены: формы, их размещение, цвета, видимость и имена. BREP-геометрия сериализуется в байты; меши передаются как данные вершин и треугольников. Вместе с параметрами отображения они образуют снимок сцены SceneSnapshot, который передаётся по межпроцессному протоколу.
GUI декодирует снимок и создаёт собственные объекты отображения AIS. Через границу процессов передаются данные геометрии, а не окно, указатели на объекты OpenCascade или объекты Qt.
Каждый запуск имеет номер поколения. GUI принимает результат актуального запуска; запоздавший результат предыдущего пересчёта не заменит текущую сцену. Во время вычисления или при ошибке остаётся видимым последний успешный результат. В анимации изменения размещения, цвета и видимости передаются отдельными обновлениями, без повторной передачи всей геометрии на каждом кадре.
Это же представление сцены используется для автоматического анализа: inspect и check могут получить результат модели без запуска viewer. Подробности — в разделах «Внутренняя кухня» и «Работа с агентом».
Типы объектов и ленивые вычисления
В ZenCad 1 ленивость была видна в типах результатов: отложенные операции могли возвращать оболочки LazyObject и LazyObjectShape. Основным представлением топологических объектов служил Shape; point3 и vector3 были классами, наследующимися от NumPy-массива.
В ZenCad 2 публичные объекты сохраняют свой тип независимо от режима вычислений. Тела, грани, рёбра и вершины представлены типами Solid, Face, Edge, Vertex; числа, точки и векторы — Scalar, Point3, Vector3 и другими типами значений. Ленивый граф хранится внутри этих объектов. Переключение deferred/immediate и включение кэша не подменяют их классы.
Для создания значений в скриптах используются строчные функции:
from zencad import *
p = point3(1, 2, 3)
v = vector3(4, 0, 0)
body = box(10)
volume = body.mass()
assert isinstance(p, Point3)
assert isinstance(body, Solid)
assert isinstance(volume, Scalar)
assert (p + v).value() == (5, 2, 3)
assert abs(float(volume) - 1000) < 1e-7
point3 теперь является функцией создания Point3, а не самим классом. Точки и векторы не являются NumPy-массивами: для получения массива используется .to_numpy(). Численное значение запрашивается через .value() или float(), форма OCP — через .native().
Запросы топологии возвращают коллекции ShapeList с определённым типом элементов. Например, body.faces() содержит Face, а body.vertices() — Vertex; координаты вершины доступны через .point(). Индексирование и селекторы сохраняют зависимости, а итерация и запрос длины коллекции требуют вычисления.
Система ленивых вычислений: EvalCache v2
ZenCad 1 использовал механизм Lazy и универсальные оболочки LazyObject. Настройки вычисления и кэширования задавались через объект zencad.lazy; пользовательские функции можно было оборачивать декоратором @lazy.
ZenCad 2 использует вычислитель EvalCache v2. Операции образуют граф с явно описанными типами результатов и правилами их сохранения. Публичный геометрический объект содержит значение или выражение этого графа. Поэтому ленивость сохраняется, но пользователь работает с объектом Solid, Point3 или Scalar, а не с универсальной оболочкой.
Зависимости могут проходить и через численные результаты. Например, объём одного тела можно использовать при построении другого, не преобразуя его заранее в Python-число:
from zencad import *
body = box(10)
volume = body.mass()
moved = body.right(volume / 100)
assert abs(float(moved.center().x) - 15) < 1e-7
volume / 100 остаётся зависимостью в графе; явный float() запрашивает вычисленное число. Граф также доступен для диагностики: inspect --tree показывает операции и зависимости, а --failed-path помогает найти путь к сбойному вычислению.
Режим выполнения и кэш задаются независимо:
set_evaluation_mode("deferred")откладывает вычисления до запроса результата;set_evaluation_mode("immediate")выполняет операции при построении.configure(cache_enabled=True)разрешает дисковый кэш, аFalseотключает его чтение и запись, не отключая сам граф зависимостей.
Сочетания этих настроек разобраны в таблице раздела «Вычисления и кэш». Файлы кэша ZenCad 1 не являются совместимым кэшем ZenCad 2.
Совместимость со старым объектом zencad.lazy и декоратором @lazy не поддерживается. Конкретные замены вызовов и правила адаптации скриптов приведены отдельно в «Миграции с ZenCad 1».