Three.js 多边形三角剖分在伪重复点中失败
Posted
技术标签:
【中文标题】Three.js 多边形三角剖分在伪重复点中失败【英文标题】:Three.js polygon triangulation fails in pseudo duplicate points 【发布时间】:2013-04-23 00:24:37 【问题描述】:在three.js 中有一个函数triangulateShape()
。现在,我遇到了使用 javascript Clipper 简化的多边形三角剖分失败。 Clipper 中的简化是使用联合来完成的。 Wikipedia article 将联合确定为找到包含两个简单多边形之一内的区域的一个或多个简单多边形。同一篇文章说,在简单多边形中,“每个顶点恰好有两条边相交”,并且还确定了一个弱简单的多边形,其中边可以相交,但没有提到边不相交但一些或多个顶点相交的边缘情况.所以有点不清楚这种情况是简单多边形还是弱简单多边形。
Clipper 选择了一种宽松的方法:简单的多边形可以有这些类似接触(或伪重复)的顶点。 This Clipper style permissive approach 导致生成的简单多边形并不像three.js:s triangulateShape()
所期望的那样简单。
下图显示了这种边缘情况的两个示例。左边的多边形是一个“简单”多边形,红点是一个“重复”。右边也是一个“简单”多边形,但红点是一个“重复”。
triangulateShape()
在这些情况下会失败,因为它会跟踪数组 allPointsMap
中的点,并从那里检查该点是否重复。要删除这些类似的重复项,我有两个选择:
OPTION 1.
更改 Javascript Clipper 内部代码以使用额外参数来处理这些问题,例如。 breakPolygonByWeakDuplicates
用于 SimplifyPolygon()
和 SimplifyPolygons()
。正如 Angus Johnson 所描述的 in his post,更改将类似于:
在 IntersectEdges() 方法中,将跟随从 ...
if ( e1Contributing && e2contributing ) 如果 ( e1stops || e2stops || (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1->polyType != e2->polyType && m_ClipType != ctXor) ) AddLocalMaxPoly(e1, e2, pt); 别的 DoBothEdges(e1,e2,pt);到...
if ( e1Contributing && e2contributing ) AddLocalMaxPoly(e1, e2, pt); AddLocalMinPoly(e1, e2, pt);更改非常简单,但原来的 Angus Johnson Clipper 和 Javascript Clipper 将不再那么兼容。当然,如果原来的 Clipper 会做出改变,Javascript Clipper 也会跟着做。
OPTION 2.
更改 three.js triangulateShape()
源代码以接受伪重复。
我的问题是:应该在哪一端完成这种额外的简化程序?一端是创建端(Clipper),另一端是三角剖分端(three.js)。
我不知道各种 3D 库中的多边形三角剖分例程,因此无法想象三角剖分例程一般有多么宽松。如果有人知道这个领域,他/她可以给出更复杂的答案。
另外我不知道其他布尔库如何处理联合或简化这种伪重复。 Clipper 允许使用简单多边形的方式肯定是有原因的(例如,与其他布尔库的兼容性),但这肯定会在 three.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。多边形仍然是一个整体,尽管它应该被分成两部分。也许安格斯·约翰逊(快船的创造者)可以提供更好的解决方案。
更新:这是一个更复杂的“简单”多边形(在 Clipper 中简化后)。所有似乎在一起的点都是完全相同的。要将其划分为真正简单的多边形,需要将其分成几部分。我的眼睛说这里有 4 个底部多边形和一个(更大的)带有孔的上部多边形,因此总体简化这将产生 5 个外部多边形和 1 个孔。或者,一个具有 5 个孔的外部多边形。或者可能是其他一些外部和孔的组合。它可以通过多种不同的方式进行简化。
小提琴在http://jsbin.com/ugimab/3(也是多边形的JSON版本)中。
这里是从 0 到 25 编号的点:
图像中2、11、14、25的顶点坐标相同,所以是“伪多顶点”。 Vertex3 不是重复的,但它触及边 6-7。
更新:
The suggested method 基于移动重复点似乎有效。如果将复制点替换为复制坐标一定距离上的两个点,产生“断笔尖”效果,三角剖分就可以了,因为生成的多边形是真正的简单多边形,这是三角剖分的要求。轮廓和孔之间以及孔和孔之间也不允许重复。下图显示了这种方法的效果。此处的距离为 10px 以显示效果,但实际上例如。 0.001 足以使多边形变得简单。 Three.js r58 中的默认三角测量器也无法按预期工作,但如果将其更改为 Poly2tri,则一切正常。这个过程在这个相当长的错误报告中描述:https://github.com/mrdoob/three.js/issues/3386。
【问题讨论】:
你能把你的源json文件分享给点。您指的是它,但链接似乎丢失了。 ["X":270,"Y":520,"X":130,"Y":490,"X":210,"Y":250 ,"X":60,"Y":170,"X":130,"Y":490,"X":20,"Y":410,"X":60, "Y":300,"X":60,"Y":20,"X":780,"Y":40,"X":680,"Y":180, "X":460,"Y":130,"X":210,"Y":250,"X":320,"Y":100,"X":220,"Y ":80,"X":210,"Y":250,"X":520,"Y":250,"X":680,"Y":180,"X ":770,"Y":480,"X":540,"Y":470,"X":520,"Y":250,"X":380,"Y": 280,"X":430,"Y":390,"X":540,"Y":470,"X":270,"Y":520,"X": 330,"Y":350,"X":210,"Y":250] 另见github issue和***.com/q/36692985/1066234 【参考方案1】:您可以编写一个函数来检测重复顶点并将它们向后移动 1px 以使它们离散(它们不再共享公共边)。这样就不会有更多的共同边缘,也不会产生错误,但视觉结果看起来还是一样的。
一种粗略的解决方案,但它可能会起作用。
【讨论】:
感谢您的回答!这个解决方案听起来确实不错。你的意思是向后=朝向前一个顶点? 是的,我很抱歉没有说明这一点。 这个解决方案似乎效果很好,如果在副本的下一条边上也添加一个额外的顶点,那么会有一点“断笔尖”的效果,这在现实世界中是不可见的.没有引起任何自相交。但是我还必须对非常复杂的多边形进行测试,然后才能确定这种方法确实可以作为三角测量之前的前置步骤。 是的。这种方法有效。如果我们首先确保在例如的距离内没有点。 0.01 个单位,则可以安全地将重复点向后移动这个量,并在重复的这个距离处添加一个额外的顶点。必须对整个轮廓孔结构进行“nibbing”,以处理跨多边形重复。并且三角测量有效,如果有缺陷的默认三角测量器更改为 tri2js:github.com/mrdoob/three.js/issues/3386. 我在 tri2js 中没有发现任何问题(至少目前还没有),但当然要求很严格:简单的多边形等。您是否发现使用 tri2js 进行三角测量有任何问题?【参考方案2】:three.js
中使用的三角测量解决方案存在几个问题。还有其他几个可用的 javascript 三角测量库,很可能在未来,当前的库将被交换为其他东西,例如 earcut.js
。有一个关于这个here in this issue on GitHub的讨论。
正如in this multi viewer here 所证明的那样,您的自相交边缘问题不是耳切问题。
如果您已经想在您的项目中使用不同的三角测量解决方案,我想参考我制作的 three.js triangulation library (an adapter)。该适配器允许将其他三个三角测量库无缝连接到您的 three.js 项目:
earcut - 耳切三角测量库 poly2tri - poly2tri 三角剖分库 libtess - libtess 镶嵌库您需要做的就是包含triangulation.js
文件:
<script src="triangulation.js"></script>
并使用setLibrary
方法设置您选择的库:
THREE.Triangulation.setLibrary('earcut');
根据您选择的库,您显然需要为库本身嵌入文件。目前对于libtess
,需要额外的tessy.js
,可以在存储库中找到。
阅读有关该项目的更多信息here on GitHub。
【讨论】:
以上是关于Three.js 多边形三角剖分在伪重复点中失败的主要内容,如果未能解决你的问题,请参考以下文章
Three.js将多边形线条(Line)转换成模型(Mesh)