Три.JS полигональная триангуляция не выполняется в псевдо-повторяющихся точках

на три.js есть функция triangulateShape(). Теперь я столкнулся с ошибкой триангуляции полигонов, которые упрощены с помощью Javascript Clipper. Упрощение в Clipper делается с помощью объединения. статья в Википедии определяет объединение как нахождение простого многоугольника или полигонов, содержащих область внутри одного из двух простых многоугольников. В той же статье говорится, что в простом многоугольнике "ровно два ребра встречаются в каждой вершине" , а также определяется слабо простой многоугольник, где ребра могут встречаться, но ничего не говорит о случае ребер, где ребра не встречаются, но некоторые или многие вершины встречаются. Поэтому немного неясно, является ли этот случай простым многоугольником или слабо простым многоугольником.

Clipper выбрал разрешительный подход: простой многоугольник может иметь такие, как касательные (или псевдо-дубликаты) вершины. это клипер стиль разрешительный подход вызывает, что сгенерированные простые полигоны не просты в смысле того, что три.js:s triangulateShape() ожидает.

на следующем рисунке показаны два примера это крайний случай. Левый многоугольник - один" простой "многоугольник, красная точка - "дубликат". Правый также является одним "простым" многоугольником, но красная точка является "дубликатом".

enter image description here

triangulateShape() не удается, в этих случаях, потому что он отслеживает точки в массиве allPointsMap и проверяет оттуда, если точка дублируется. Чтобы удалить эти дубликаты, у меня есть два опции:


OPTION 1.

изменить внутренний код JavaScript Clipper для обработки их с помощью дополнительного параметра, например. breakPolygonByWeakDuplicates на SimplifyPolygon() и SimplifyPolygons(). Как описал Ангус Джонсон в должности изменение будет что-то вроде:

в методе IntersectEdges() измените значение follow from ...

if ( e1Contributing && e2contributing )
{
  if ( e1stops || e2stops || 
    (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
    (e1->polyType != e2->polyType && m_ClipType != ctXor) )
      AddLocalMaxPoly(e1, e2, pt); 
  else
      DoBothEdges( e1, e2, pt );
}

to ...


if ( e1Contributing && e2contributing )
{
    AddLocalMaxPoly(e1, e2, pt); 
    AddLocalMinPoly(e1, e2, pt);
}

изменение очень легко, но после этого первоначальный Клипер Ангуса Джонсона и Javascript Clipper больше не будет таким совместимым. Конечно, если оригинальный Clipper внесет изменения, Клипер Javascript будет следовать за ним.


OPTION 2.

изменить три.js triangulateShape() исходный код для приема также псевдо-дубликатов.


мой вопрос: в какой конец это как дополнительная процедура упрощения должно быть сделано? Первый конец-сторона создания (клипер) , а другой конец-сторона триангуляции (три.в JS).

я не знаю процедур полигональной триангуляции в различных 3D-библиотеках, поэтому не могу себе представить, насколько разрешительны процедуры триангуляции в целом. Если кто-то знает эту область, он/она может дать более сложный ответ.

также я не знаю, как другие логические библиотеки обрабатывают объединение или упрощение этого, как псевдо-дубликаты. Есть, конечно, причина, почему Клипер носит разрешительный средства простой многоугольник (например. совместимость с другими boolean libraries), но, безусловно, это создает проблемы при триангуляции полигонов в трех.js.

для справки здесь триангулирующий код из трех.js:

triangulateShape: function ( contour, holes ) {

    var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );

    var shape = shapeWithoutHoles.shape,
        allpoints = shapeWithoutHoles.allpoints,
        isolatedPts = shapeWithoutHoles.isolatedPts;

    var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape

    // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.

    //console.log( "triangles",triangles, triangles.length );
    //console.log( "allpoints",allpoints, allpoints.length );

    var i, il, f, face,
        key, index,
        allPointsMap = {},
        isolatedPointsMap = {};

    // prepare all points map

    for ( i = 0, il = allpoints.length; i < il; i ++ ) {

        key = allpoints[ i ].x + ":" + allpoints[ i ].y;

        if ( allPointsMap[ key ] !== undefined ) {

            console.log( "Duplicate point", key );

        }

        allPointsMap[ key ] = i;

    }

    // check all face vertices against all points map

    for ( i = 0, il = triangles.length; i < il; i ++ ) {

        face = triangles[ i ];

        for ( f = 0; f < 3; f ++ ) {

            key = face[ f ].x + ":" + face[ f ].y;

            index = allPointsMap[ key ];

            if ( index !== undefined ) {

                face[ f ] = index;

            }

        }

    }

    // check isolated points vertices against all points map

    for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {

        face = isolatedPts[ i ];

        for ( f = 0; f < 3; f ++ ) {

            key = face[ f ].x + ":" + face[ f ].y;

            index = allPointsMap[ key ];

            if ( index !== undefined ) {

                face[ f ] = index;

            }

        }

    }

    return triangles.concat( isolatedPts );

}, // end triangulate shapes

обновление: я сделал один SVG http://jsbin.com/ugimab/1 где пример многоугольника, который имеет точку (150,150), которая является слабым дубликатом или псевдо-дубликатом. Ниже приведены различные способы представления этого многоугольника:

var weakDuplicate1 = [{"X":100,"Y":200},{"X":150,"Y":150},{"X":100,"Y":100},{"X":200,"Y":100},{"X":150,"Y":150},{"X":200,"Y":200}];

var weakDuplicate2 = [100,200, 150,150, 100,100, 200,100, 150,150, 200,200];

var weakDuplicate3 = "M100,200 L150,150 L100,100 L200,100 L150,150 L200,200Z";


обновление: если кто-то удалось найти решение для триангуляции также полигонов, которые имеют это как слабо повторяющиеся точки, было бы очень полезно, если бы вы опубликовали свои результаты.


UPDATE: протестирован Вариант 1, но он не был успешным:http://jsbin.com/owivew/1. Полигон остается единым целым, хотя и должен быть разделен на две части. Возможно, у Ангуса Джонсона (создателя клипера)есть лучшее решение.


UPDATE: вот еще сложный" простой " многоугольник (после упрощения в Clipper). Все точки, которые кажутся вместе, абсолютно идентичны. Чтобы разделить это на действительно простые многоугольники, потребуется разделить его на части. Мои глаза говорят, что здесь 4 нижних многоугольника и один (больший) верхний многоугольник с отверстием, так как общее упрощение приведет к 5 внешним многоугольникам и 1 отверстию. Или, альтернативно, один внешний многоугольник с 5 отверстиями. Или, возможно, какая-то другая комбинация внешних и внутренних отверстий. Это может быть упрощено по-разному.

скрипка в http://jsbin.com/ugimab/3 (также JSON-версия полигона).

enter image description here

и вот точки, пронумерованные от 0 до 25:

enter image description here

в изображении вершины 2,11,14,25 имеют одинаковую координату, поэтому это "псевдо-множественная вершина". Vertex3 не является дубликатом, но он касается края 6-7.


обновление:

предлагаемый метод это основано на перемещении дубликатов точек, похоже, работает. Если дублирующая точка заменяется двумя точками, которые находятся на определенном расстоянии от дублирующей координаты, создавая эффект "сломанного пера", триангуляция работает, потому что полученные полигоны являются истинными простыми полигонами, что является требованием для триангулятора. Также не допускаются дубликаты между контуром и отверстиями ни между дырами, ни между дырами. На следующем рисунке показан результат этого метода. Расстояние здесь 10px, чтобы показать эффект, но на самом деле, например. 0.001 достаточно, чтобы сделать простые многоугольники. Также по умолчанию триангулятор в три.js r58 работает не так, как ожидалось, но если он изменен на Poly2tri, то все хорошо. Процесс описан в этом довольно длинном отчете об ошибке:https://github.com/mrdoob/three.js/issues/3386.

enter image description here

2 ответов


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

вид грубого решения, но это может сработать.


существует несколько проблем с решением триангуляции, используемым в three.js. Существует несколько других библиотек триангуляции javascript, и вполне возможно, что в будущем текущая библиотека будет заменена на что-то еще, например earcut.js. Существует дискуссия об этом вот в этом выпуске на GitHub.

ваша проблема само пересекающихся краев не является проблемой для earcut, как показано в этом мульти просмотра вот!--10-->.


если вы уже хотите использовать другое решение триангуляции в своем проекте я хотел бы обратиться к три.библиотека триангуляции js (адаптер) я сделал. Адаптер позволяет легко подключать три другие библиотеки триангуляции к вашим трем.проект js:

  • earcut - библиотека триангуляции earcut
  • poly2tri - триангуляция poly2tri библиотека
  • libtess - библиотека тесселяции libtess

все, что вам нужно сделать, это включить :

<script src="triangulation.js"></script>

и установить библиотеку по вашему выбору с помощью setLibrary способ:

THREE.Triangulation.setLibrary('earcut');

в зависимости от выбранной вами библиотеки Вам, очевидно, нужно будет встроить файлы для самой библиотеки. В настоящее время для libtess дополнительные tessy.js нужен, который можно найти в репозитории.

подробнее о проекте здесь на GitHub.