Выбрать главу
Схема кода сшивания рёберных циклов

В функции, которая создает новые грани, мы должны выполнить следующие шаги:

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)