Three.js多边形三角剖分在伪重复点上失败

7
three.js中有一个名为triangulateShape()的函数。现在我遇到了使用Javascript Clipper简化多边形时无法三角剖分的问题。在Clipper中进行简化是通过Unioning完成的。维基百科文章将联合定义为查找包含两个简单多边形内部区域的简单多边形或多边形。同一篇文章说,在简单多边形中,“每个顶点恰好有两条边相交”,并确定了弱简单多边形,其中边可以相交,但未提及边缘不相交的情况,但某些或许多个顶点相交。因此,这种情况是否为简单多边形还有些不清楚。

Clipper选择了宽容的方法:简单多边形可以具有这些类似接触(或伪重复)的顶点。这种Clipper风格的宽容方法导致生成的简单多边形不符合three.js的triangulateShape()所期望的简单多边形的含义。

以下图片展示了这种边缘情况的两个例子。左边的多边形是一个“简单”的多边形,红点是一个“重复”的点。右边的多边形也是一个“简单”的多边形,但红点是一个“重复”的点。

enter image description here

triangulateShape()在这些情况下会失败,因为它在数组allPointsMap中跟踪点并从那里检查点是否重复。为了删除这些类似的重复项,我有两个选择:


选项1.

更改Javascript Clipper内部代码,使用额外参数来处理这些问题,例如breakPolygonByWeakDuplicates用于SimplifyPolygon()SimplifyPolygons()。正如Angus Johnson在他的帖子中所描述的那样,更改将类似于:

在IntersectEdges()方法中,将以下内容更改为...

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 );
}

变成...

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

这个更改非常简单,但原始的Angus Johnson Clipper和Javascript Clipper将不再兼容。当然,如果原始Clipper进行更改,Javascript Clipper将跟随它。


选项 2.

将 three.js 的 triangulateShape() 源代码更改为接受伪重复内容。


我的问题是:这种额外的简化程序应该在哪一端进行?第一个端点是创建端(Clipper),另一个端点是三角剖分端(three.js)。 我不知道各种3D库中的多边形三角剖分程序,因此无法想象通常情况下三角剖分程序有多宽松。如果有人了解这个领域,他/她可以给出更复杂的答案。
此外,我不知道其他布尔库如何处理联合或简化类似伪重复的内容。毫无疑问,Clipper在简单多边形方面很宽容(例如与其他布尔库的兼容性),但这绝对会在三.js中对多边形进行三角剖分时造成问题。
以下是three.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";

更新:如果有人已经成功找到了解决方法来三角化那些具有弱重复点的多边形,如果您能公开您的发现,那将非常有帮助。


更新:测试了选项1,但没有成功:http://jsbin.com/owivew/1。多边形仍然是一个整体,尽管它应该被分成两部分。也许Angus Johnson(Clipper的创建者)有更好的解决方案提供。


更新: 这里有一个更复杂的“简单”多边形(在 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具有相同的坐标,因此它是一个“伪多重顶点”。顶点3不是重复的,但它与边6-7相接触。

更新:

基于移动重复点的建议方法似乎有效。如果用距离重复坐标一定距离的两个点替换重复点,会产生“破碎钢笔尖”效果,三角剖分可以工作,因为产生的多边形是真正的简单多边形,这是三角剖分器的要求。轮廓和孔之间以及孔与孔之间也不允许有重复。下面的图像显示了该方法的效果。距离此处为10px以显示效果,但实际上0.001就足以使多边形简单。另外,Three.js r58中的默认三角剖分器无法按预期工作,但如果将其更改为Poly2tri,则一切都正常。该过程在这份相当长的错误报告中有描述:https://github.com/mrdoob/three.js/issues/3386

enter image description here


你能分享一下带有点的源JSON文件吗?你在引用它,但链接好像丢失了。 - Wilt
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Timo Kähkönen
请参阅GitHub问题和https://stackoverflow.com/q/36692985/1066234。 - Avatar
2个回答

3

您可以编写一个函数来检测重复的顶点并将它们向后移动1px,使它们离散化(不再共享公共边)。这样就不会有公共边了,也不会产生错误,但视觉效果仍然相同。

这种方法有些粗糙,但可能有效。


1
谢谢你的回答!这个解决方案听起来确实不错。你是指backward = 向前一个顶点吗? - Timo Kähkönen
是的,确切地说,我很抱歉没有说明。 - nicholaswmin
1
这个解决方案似乎非常好,如果在重复边的下一个边上添加一个额外的顶点,那么就会产生一种微小的“断笔尖”效果,在现实世界中是看不见的。没有引起任何自相交。但在进行三角剖分之前,我必须对真正复杂的多边形进行测试,以确保这种方法确实可以作为预处理步骤。 - Timo Kähkönen
我尚未发现 tri2js 存在任何问题(至少目前为止),但当然要求非常严格:只能是简单多边形等。你是否在使用 tri2js 进行三角剖分时发现了任何问题? - Timo Kähkönen
还没有,现在提到这个是相当仓促的决定。好吧,至少我们知道如果 tri2js 出现任何问题应该去哪里寻找帮助。 - nicholaswmin
显示剩余2条评论

0

three.js中使用的三角剖分解决方案存在几个问题。还有其他几个JavaScript三角剖分库可用,未来当前库可能会被替换为其他库,例如earcut.js。关于这个问题在GitHub上有这里的讨论

您遇到的自相交边问题对于earcut来说不是问题,可以在这个多视图演示中演示。


如果您想在项目中使用不同的三角剖分解决方案,我想推荐一个three.js三角剖分库(适配器)。该适配器允许将三个其他三角剖分库无缝连接到您的three.js项目中:

您只需要包含triangulation.js文件即可:

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

使用setLibrary方法设置您选择的库:

THREE.Triangulation.setLibrary('earcut');

根据您选择的库,您显然需要嵌入库本身的文件。目前对于libtess,需要额外的tessy.js,可以在存储库中找到。

了解更多关于该项目的信息,请点击这里在GitHub上


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接