javascript - 在 3D 空间中实现扩展多面体算法

标签 javascript collision-detection

我正在尝试在 3D 空间中实现 EPA 算法,但我似乎发现了凸单纯形可以变成凹单纯形的情况。

考虑这个单纯形:

enter image description here

而且因为很难看到这里发生了什么,所以它是动画的:



原点是红、绿、蓝轴助手。没有连接边缘的白色球体代表我需要将多面体扩展到下一个点。 5 个黄色箭头是应删除的面的法线,因为它们与新点的原点方向相同。有些面孔看起来不在同一个方向,但我已经验证它们是面部正常和新点的点积:

  • 0.45396564417079877
  • 0.9473689548609279
  • 0.3346846050014339
  • 0.09982613239032267
  • 0.09982617482390854

  • 所以 .gif 右侧的那两张脸只是同一方向的大麦。

    好的,所以 EPA 算法说要删除这些面孔:



    然后使用我们移除的面的剩余边为新点构建新面。但是你现在可以看到凸单纯形变成了凹单纯形:



    这显然是不对的,但我不确定我哪里出错了。对我来说,感觉就像我删除了我不应该拥有的面孔,但这些面孔与新点的方向相同。

    这是我的代码:
    var EPA = function(aWorldVerts, bWorldVerts, simplex) {
        var simplexFaces = [{a: 0, b: 1, c: 2},
                            {a: 0, b: 1, c: 3},
                            {a: 0, b: 2, c: 3},
                            {a: 1, b: 2, c: 3}];
    
        var ret = null;
    
        while(true) {
            var face = findClosestFace(simplex, simplexFaces);
            var point = support(aWorldVerts, bWorldVerts, face.norm);
            var dist = point.clone().dot(face.norm);
    
            if(dist - face.dist < 0.00001) {
                ret = {axis: face.norm, dist: dist};
                break;
            }
    
            simplex.push(point);
            reconstruct(simplex, simplexFaces, point);
        }
    
        return ret;
    }
    
    var reconstruct = function(simplex, simplexFaces, extendPoint) {
        //I do realize that this function can be done more efficietly
        var removalFaces = [];
        for(var i = 0; i < simplexFaces.length; i++) {
            var face = simplexFaces[i];
    
            var ab = simplex[face.b].clone().sub(simplex[face.a]);
            var ac = simplex[face.c].clone().sub(simplex[face.a]);
            var norm = ab.cross(ac).normalize();
    
            var a0 = new THREE.Vector3().sub(simplex[face.a]);
            if(a0.dot(norm) > 0)
                norm.negate();
    
            if(extendPoint.clone().dot(norm) > 0) {
                removalFaces.push(i);
            }
        }
    
        //get the edges that are not shared between the faces that should be removed
        var edges = [];
        for(var i = 0; i < removalFaces.length; i++) {
            var face = simplexFaces[removalFaces[i]];
            var edgeAB = {a: face.a, b: face.b};
            var edgeAC = {a: face.a, b: face.c};
            var edgeBC = {a: face.b, b: face.c};
    
            var k = edgeInEdges(edges, edgeAB);
            if(k != -1)
                edges.splice(k, 1);
            else
                edges.push(edgeAB);
    
            k = edgeInEdges(edges, edgeAC);
            if(k != -1)
                edges.splice(k, 1);
            else
                edges.push(edgeAC);
    
            k = edgeInEdges(edges, edgeBC);
            if(k != -1)
                edges.splice(k, 1);
            else
                edges.push(edgeBC);
        }
    
        //remove the faces from the polytope
        for(var i = removalFaces.length - 1; i >= 0; i--) {
            simplexFaces.splice(removalFaces[i], 1);
        }
    
        //form new faces with the edges and new point
        for(var i = 0; i < edges.length; i++) {
            simplexFaces.push({a: edges[i].a, b: edges[i].b, c: simplex.length - 1});
        }
    }
    
    var edgeInEdges = function(edges, edge) {
        for(var i = 0; i < edges.length; i++) {
            if(edges[i].a == edge.a && edges[i].b == edge.b)
                return i;
        }
    
        return -1;
    }
    
    var findClosestFace = function(simplex, simplexFaces) {
        var closest = {dist: Infinity};
    
        for(var i = 0; i < simplexFaces.length; i++) {
            var face = simplexFaces[i];
    
            var ab = simplex[face.b].clone().sub(simplex[face.a]);
            var ac = simplex[face.c].clone().sub(simplex[face.a]);
            var norm = ab.cross(ac).normalize();
    
            var a0 = new THREE.Vector3().sub(simplex[face.a]);
            if(a0.dot(norm) > 0)
                norm.negate();
    
            var dist = simplex[face.a].clone().dot(norm);
    
            if(dist < closest.dist) {
                closest = {index: i, dist: dist, norm: norm, a: face.a, b: face.b, c: face.c};
            }
        }
    
        return closest;
    }
    
    var support = function(aVerts, bVerts, dir) {
        a = getFurthestPointInDirection(aVerts, dir);
        b = getFurthestPointInDirection(bVerts, dir.clone().negate());
        return a.clone().sub(b);
    }
    
    var getFurthestPointInDirection = function(verts, dir) {
        var index = 0;
        var maxDot = verts[index].clone().dot(dir.clone().normalize());
    
        for(var i = 1; i < verts.length; i++) {
            var dot = verts[i].clone().dot(dir.clone().normalize());
    
            if(dot > maxDot) {
                maxDot = dot;
                index = i;
            }
        }
    
        return verts[index];
    }
    

    我知道支持功能和 findClosestFace() 一样正常工作和 edgeInEdges() .此外,这应该没关系,但这是使用 Three.js 和 Javascript 实现的。也许我只是从根本上误解了算法的工作原理?

    我做错了什么,我该如何解决?

    最佳答案

    经过数小时的调试后,我发现了我的问题。通过检查面的法线是否与新点的原点方向相同,找不到在将多面体延伸到新点之前要移除的面。许多关于这个主题的文章都说你想删除新点可以“看到”的面,我认为这意味着法线在同一个方向上。情况并非如此,因为您可以很好地使面法线与新点的原点方向相同,但是该点无法“看到”该面,因此将其删除将是有问题的,这就是我正在做的.你想从本质上想象你的相机正好在新点的位置,环顾四周,你能看到的任何脸都应该被移除。

    要检查新点是否可以“看到”给定面,您需要形成从所述面的顶点到新点的向量,并检查该点与面法线的点积。所以我换了if(extendPoint.clone().dot(norm) > 0)if(norm.clone().dot(extendPoint.clone().sub(simplex[face.a])) > 0)reconstruct()功能,它现在可以工作了。

    关于javascript - 在 3D 空间中实现扩展多面体算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48979868/

    相关文章:

    c++ - Box2D:程序在破坏 b2Body 时崩溃

    java - 两个 Shape 对象似乎在错误的位置相交?

    javascript - Mongoose :排序

    javascript - 带 Angular 过滤器的条件货币符号

    javascript - 获取json编码形式的信息

    javascript - 三.js : Box3 from an object not in scene

    c++ - 使用 C++ 在 Box2D 中查找碰撞的物体

    javascript - Protractor 无法识别打开的 native 警报

    javascript - 正则表达式在函数中是如何工作的?

    javascript - p5.j​​s 中的 Angular 碰撞 Angular