В продолжение
этой темы докладываю о полученных мной отрицательных результатах в области разработки универсального механизма гладкой склейки кусков 3D-поверхности (сплайнов).
Напоминаю постановку задачи. Есть
рисовалка Asymptote, с помощью которой Вы можете создавать красивую 3D-графику для использования в своих книгах и статьях. Asymptote составляет поверхность 3D-объекта из кусочков (т.н. "патчей"), каждый из которых представляет собой четырёхугольный сплайн, точки которого определяются уравнением:
, где
, а
- полином 3-ей степени по каждой из этих двух переменных. Для определения сплайна достаточно определить 16 точек: 4 - углы сплайна, по 2 точки на каждую из 4 сторон определяют геометрию этих сторон и ещё 4 внутренние точки определяют "внутреннюю" геометрию сплайна. Во многих типичных случаях для более или менее простых геометрических фигур можно подобрать патчи таким образом, чтобы они стыковались гладко. В упомянутой выше теме приведен пример гладкой склейки сферы из 8 кусочков. Но хотелось бы иметь возможность строить сложные поверхности через произвольно расставленные точки, чтобы натянутые на эти точки патчи стыковались друг с другом гладко.
Разработчики Asymptote предложили для этого пакет
smoothcontour3.asy, в котором есть функция
patchwithnormals, назначение которой вроде бы и заключается в том, чтобы построить "правильный" патч. Функция принимает на вход замкнутую 4-сегментную линию, которая должна стать границей патча, и 4 массива по 3 вектора каждый, которые определяют нормали к поверхности в 3-х точках каждой из 4-х его сторон. Логика авторов была такова: 12 определяющих сплайн точек из 16-ти однозначно определены его границей. Оставшиеся 4 внутренние точки - это 12 независимых переменных, которые мы можем варьировать, чтобы подобрать направления нормалей к поверхности в 12 точках на её границе (понятно, что в 4-х углах направления нормалей уже определены формой границы). Осталось, вроде бы, придумать алгоритм для определения "правильных" направлений этих нормалей. Очевидно, что если для двух соседних патчей на границе между ними направления нормалей к поверхностям совпадут в трёх точках (плюс в двух углах, которые у них общие), то стыковка получится довольно гладкой.
Я написал программулину (на той же Asymptote), которая определённым образом расставляет векторы нормалей в трёх точках линии в зависимости от того, как они расставлены на её концах. На рисунке эти векторы изображены красным цветом. Тем не менее, при попытке применить функцию
patchwithnormals я получил какую-то ерунду. Помимо того, что реальные векторы нормалей сильно не совпали с заданными, поверхность вообще получилась крайне безобразной (хотя граница не слишком корявая). Выяснив таким образом, что предложенная разработчиками Asymptote процедура не работает (за исключением, может быть, каких-то идеальных случаев), я решил поискать собственное решение.
Я определил функцию ощибки как сумму квадратов расстояний между концами заданных и реальных векторов нормалей (разумеется, все векторы нормалей - единичной длины). Далее, я попытался найти минимум этой функции 12-ти переменных (коими являются координаты 4-х внутренних точек сплайна). Применялись методы Нелдера-Мида, Хука-Дживса и градиентного спуска (с некоторыми модификациями, в том числе, в целях борьбы с оврагами). Лучший результат, которого мне удалось достичь на пробной поверхности, показан на рисунке синими векторами, изображающими реальные нормали. Как видно, расхождения с заданными (красными) векторами довольно велики.
Я предположил, что результаты могут быть лучше, если выбирать границу поверхности каким-либо специальным образом. Например, можно ограничится плоскими кривыми. На рисунке как раз изображён такой случай - границ, которые являются плоскими кривыми. Увы, результат всё ещё далёк от идеала. Отсюда я осмеливаюсь предположить, что
в классе сплайнов 3-его порядка не существует приемлемого по качеству решения для гладкой склейки (за исключением частных случаев).