В функции, которая создает новые грани, мы должны выполнить следующие шаги:
1. Удостовериться, что оба рёберных цикла цикла имеют одинаковую и ненулевую длину.
2. Для каждого ребра в цикле 1:
1. Найти ребро в цикле 2, которое ближе всего.
2. Создать грань, соединяющую эти два ребра.
Функция, которая осуществляет эту довольно сложную на вид схему:
def bridge_edgeloops(e1,e2,verts):
e1 = e1[:]
e2 = e2[:]
faces=[]
if len(e1) == len(e2) and len(e1) > 0 :
Функция принимает аргументы: два списка рёбер и список вершин. Рёбра представлены в виде кортежей двух целых (индексы в списке вершин verts), а вершины - в виде кортежей координат x, y, и z.
Первая вещь, которую мы сделаем - создадим копии двух рёберных списков, поскольку мы не хотим испортить списки в их оригинальном контексте. Список граней, который мы будем строить, инициализируется в пустой список, и мы проверяем разумность и равенство длин обоих рёберных списков. Если это подтверждается, мы приступаем к следующему куску:
for a in e1:
distance = None # расстояние
best = None # лучший
enot = [] # отвергнутые рёбра
Мы повторяем по каждому ребру в первом списке, ссылаясь на это ребро через a. параметр distance содержит расстояние до ближайшего ребра во втором рёберном списке, а best будет ссылкой на это ребро. enot - список, который копит все рёбра из второго списка, которые находятся на большем расстоянии, чем наилучшее.
В конце каждой итерации, enot будет содержать все рёбра из второго списка минус одно - которое мы считаем ближайшим. Затем мы переназначаем enot на второй список, таким образом второй список уменьшается на одно ребро с каждой итерацией. Мы заканчиваем, как только второй список рёбер будет исчерпан:
while len(e2):
b = e2.pop(0)
Текущее ребро из второго списка, которое мы рассматриваем, называется b. Для наших целей, мы определяем расстояние между a и b как сумму расстояний между соответствующими вершинами в a и b. Также мы проверяем, не окажется ли короче сумма расстояний до перевёрнутых вершин b. Если получилась такая ситуация, мы меняем вершины в ребре b. Это может казаться сложным способом действий, но суммированием двух расстояний мы гарантируем, что рёбра, которые сравнительно коллинеарны (параллельны) - привилегированы, тем самым уменьшая число неплоских граней, которые будут созданы. Проверяя, не приведёт ли перевёрнутый второй край к более короткому расстоянию, мы предотвращаем образование искорёженного в виде галстука-бабочки четырёхугольника, как проиллюстрировано на следующем рисунке:
Реализация будет выглядеть похоже на предшествующий рисунок, где выделенные вектора - псевдонимы на объект Mathutil.Vector, преобразующий наши кортежи с координатами x, y, и z в соответствующие векторы, которые мы можем вычитать, складывать, и получать их длину.
Сначала мы вычисляем расстояние:
d1 = (vec(verts[a[0]]) - vec(verts[b[0]])).length + \
(vec(verts[a[1]]) – vec(verts[b[1]])).length
Затем мы проверяем с перевёрнутым ребром b, будет ли в результате расстояние короче:
d2 = (vec(verts[a[0]]) - vec(verts[b[1]])).length + \
(vec(verts[a[1]]) - vec(verts[b[0]])).length
if d2<d1 :
b =(b[1],b[0])
d1 = d2
Если рассчитанное расстояние не самое короткое, мы откладываем ребро для следующей итерации, если оно не первое, с которым мы столкнулись:
if distance == None or d1<distance :
if best != None:
enot.append(best)
best = b
distance = d1
else:
enot.append(b)