Чем 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 помогает найти путь к сбойному вычислению.

Режим выполнения и кэш задаются независимо:

Сочетания этих настроек разобраны в таблице раздела «Вычисления и кэш». Файлы кэша ZenCad 1 не являются совместимым кэшем ZenCad 2.

Совместимость со старым объектом zencad.lazy и декоратором @lazy не поддерживается. Конкретные замены вызовов и правила адаптации скриптов приведены отдельно в «Миграции с ZenCad 1».