Manifold Geometry // Многообразная Геометрия

Лучшие практики

Подписаться на эту рубрику по RSS

Делай так.

Императивный плоттер

Преамбула

Это еще одна заметка из серии, которую можно назвать методологической. Мы продолжаем говорить о способах вдумчивой и основательной реализации алгоритмов вычислительной геометрии и геометрического моделирования. Чтобы двигаться в этом направлении, полезно выбрать рабочую метафору для такого рода алгоритмов. Ранее мы позаимствовали эту метафору у разработчиков ACIS, представив наукоемкое ПО как экосистему, а единичный алгоритм в ней, как живое существо, сформированное ЭВОЛЮЦИОННО, чтобы наилучшим образом отвечать ВЫЗОВАМ СРЕДЫ. Алгоритмы группируются в ВЫЧИСЛИТЕЛЬНЫЕ СХЕМЫ для решения конкретных практических задач. Вычислительная схема — это составной алгоритм, обеспечивающий, помимо прочего, и лучшую ВЫЖИВАЕМОСТЬ решения.

Из понятия о вычислительной схеме немедленно следует понятие о МОДУЛЬНОЙ АРХИТЕКТУРЕ алгоритмов. Чем ограниченнее специализация составных алгоритмических блоков, тем проще их поддерживать и отлаживать. Одним из основных средств отладки геометрического алгоритма является построение рабочего чертежа на каждом этапе его исполнения. Ниже мы вводим понятие императивного плоттера для решения этой задачи.

Определение

Императивный плоттер — это подпрограмма визуализации, выполняющая команды НЕМЕДЛЕННОЙ отрисовки. Идея императивной визуализации предполагает, что на произвольном этапе алгоритма любой вовлеченный в него геометрический примитив может быть выведен на экран для профилировки. Пример псевдо-кода императивной визуализации представлен ниже:

{
  // ...
  // Here goes some code to build two surfaces S1 and S2
  // ...
  
  IV.DRAW_SURFACE(S1)
  IV.DRAW_SURFACE(S2)
  
  // ...
  // Here goes some code to intersect two surfaces S1 and S2
  // ...
  
  IV.CLEAN
  IV.DRAW_CURVE(intersection_curve)
}

В приведенном листинге объект IV представляет плоттер. Имеет смысл расставить императивные вызовы, следуя логике геометрического алгоритма и обеспечив их холостую отработку в случае, если профилировка отключена. Так же, как и в случае журналирования алгоритмов, подобные инъекции плоттера позволяют сократить издержки на отладку алгоритма, которая, вообще говоря, никогда не заканчивается из-за эволюционного характера разработки систем подобного рода.

В библиотеке OpenCascade роль императивного визуализатора играет плоттер AXO приложения Draw. Заметим, что это крайне примитивный, псевдо-трехмерный вьювер, не использующий OpenGL. Его интерактивность реализована при помощи команд, подаваемых с клавиатуры — старая школа... Кроме того, единственным режимом отображения является проволочный (wireframe), а производительность на больших моделях оставляет желать лучшего. Продвинутый интерактивный визуализатор OpenCascade (с использованием OpenGL), в свою очередь, императивным не является, так как требует от программиста оперирования понятием СЦЕНЫ, ИНТЕРАКТИВНОГО ОБЪЕКТА, а так же его СОСТОЯНИЙ и ЖИЗНЕННОГО ЦИКЛА.

Хорошая императивная визуализация должна работать по принципу ученической доски, с которой в любой момент можно что угодно стереть и что угодно нарисовать (цветными мелками). Нарисованные объекты, хотя и могут иметь некоторую объектно-ориентированную инфраструктуру, не требуют от программиста какой-либо деятельности по ее поддержанию. Императивный визуализатор может быть получен из неимперативного выведением соответствующего API.

Первые разработчики OpenCascade, по видимому, знали толк в геометрических алгоритмах. Среда Draw, действительно, крайне удобна и бесконечно гибка для прототипирования и отладки. Однако средства императивной визуализации в ней на сегодняшний день уже морально устарели, поэтому вопрос реализации новой интерактивной отладочной среды, можно сказать, назрел.

API императивного плоттера

Займемся проектированием императивного плоттера. Начнем с выделения его базового прикладного интерфейса. Будем записывать команды в следующем виде:

COMMAND_NAME
<- ARGUMENTS

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

Геометрия

DRAW_POINTS
<- [double (X1), double (Y1), double (Z1), double (X2), ...]
DRAW_LINK
<- [gp_Pnt, gp_Pnt]
DRAW_CURVE
<- Handle(Geom_Curve)
DRAW_SURFACE
<- Handle(Geom_Surface)

Топология

DRAW_SHAPE
<- TopoDS_Shape

Атрибуты

Атрибуты, такие как цвет или прозрачность, не настраиваются отдельно, а указываются для каждого объекта индивидуально в момент отрисовки. Таким образом режим «кисти» здесь не используется, чтобы избавить императивный принцип от подобия памяти, за которой пришлось бы следить (например, не забыть сбросить цвет после его временной установки). Команда должна давать результат, полностью определяемый ее аргументами и ничем более. Пример:

Handle(Geom_Surface) surface = ...
Quantity_Color       color   = Quantity_NOC_SPRINGGREEN;
const double         opacity = 0.5;
  
IV.DRAW_SURFACE(surface, color, opacity, "My Surface");

Отдельно отметим возможность именования примитивов. Использование этой возможности приводит к тому, что профилируемый алгоритм ПРИОБРЕТАЕТ СВОЙ ЯЗЫК, как набор имен переменных, имеющих явное геометрическое представление. Сессия профилировки, обсуждение алгоритма с коллегами, документирование — все эти процессы могут выполняться с использованием собственного языка алгоритма или его специфического жаргона (как угодно).

Возможная архитектура императивного плоттера

Автор использовал следующую архитектуру:

  1. Собственно ВИЗУАЛИЗАЦИЯ средствами VTK.
  2. МОДЕЛЬ ДАННЫХ, реализованная средствами Active Data.
Имеет смысл абстрагировать API плоттера от конкретных библиотек. Это позволит сохранить вызовы плоттера в коде алгоритмов без внесения дополнительных зависимостей.

Связка между моделью данных и конвейерами VTK позволяет выстроить автоматический механизм ленивой отрисовки, причем:

  1. Данные могут быть сохранены в файл, т.е. плоттер поддерживает гибернацию.
  2. Имеется встроенная поддержка Undo/Redo в силу транзакционности самой модели данных.
  3. Каждый элемент модели данных имеет временную метку MTime (Modification Time), позволяющую перестраивать конвейеры VTK только по необходимости.
  4. Элементы модели данных — единственное, что можно отрисовать. Поэтому все допустимые примитивные элементы чертежа должны иметь свои модельные представления.

Рис. 1. Примерная модель данных императивного плоттера.

Императивные вызовы, такие как DRAW_SURFACE, приводят к тому, что создается новый объект модели данных. Каждый такой объект, в свою очередь, связан с некоторым Объектом-Представлением, умеющим рисовать модельные данные. Плодотворно показал себя подход, при котором одному типу модельного объекта соответствует ТОЛЬКО ОДИН тип Представления. Представление — это, грубо говоря, чертеж и средства его построения. В уме можно держать нестрогую аналогию между Представлением и актором VTK (на самом деле Представление — это набор акторов, отвечающих данному объекту). Разумеется, такая архитектура есть всего-лишь способ сложить 2 и 2, и объектную модель можно изобрести какую угодно.

Рис. 2. Результат работы императивного плоттера, содержащий точки (красный цвет), линии (голубой), поверхности (зеленый полупрозрачный) и твердотельные B-Rep объекты (серый).

Дополнительные возможности

Кратко перечислим нерассмотренные возможности императивного плоттера:

  1. Очищение сцены.
  2. Интерактивная селекция с подсветкой и отображением имени выбранного объекта.
  3. Возможность скрыть и показать ТОЛЬКО ОДИН объект или группу объектов.
  4. Возможность сохранить в файл геометрию выбранного объекта.
  5. Различные режимы визуализации объектов.

Заключение

Императивный плоттер — это еще один инструмент из джентельменского набора вычислительного геометра. По существу это скорее практика, следование которой помогает в разработке и сопровождении индустриальных алгоритмов. Эта практика — одна из многих. Сюда же можно отнести журналирование алгоритмов и, разумеется, юнит-тестирование, без которого надежный софт невозможно себе представить. Есть еще и неинструментальные «золотые правила», которые гораздо легче вербализовать, чем алгоритмически оформить. Одну такую заповедь мы уже приводили в заметке о булевых операциях: «don't ask the same geometric question more than once». Все эти тонкости и приемы, будучи восприняты одним человеком, формируют своеобразную вычислительную культуру, без которой разработка сложных алгоритмических схем похожа скорее на перебегание минного поля, чем на творческую деятельность.

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