代码之家  ›  专栏  ›  技术社区  ›  ceremcem

如何在笔划边界之间执行碰撞检测

  •  1
  • ceremcem  · 技术社区  · 6 年前

    Sketch

    我只想在橙色圆圈足够靠近(灰色圆圈接触)线时,警报框变为红色:

    enter image description here

    但是,当前它可能会抛出假警报,即在足够接近之前:

    enter image description here

    3 回复  |  直到 6 年前
        1
  •  1
  •   arthur.sw    6 年前
        2
  •  1
  •   ceremcem    6 年前

    Full example

    // Returns item.position at the moment of collision
    // and the collided object.
    function collisionTest(item, curr){
        var prev = item.position;
        var point = curr.clone();
        var hit = null;
        var tolerance = 45;
        var _error = 0.01;
        var firstPass = true;
        var _stopSearching = false;
        var _step = ((curr - prev) / 2).clone();
        var _acceptable_iter_count = 16;
        var _max_iter_count = 100;
    
        var i; 
        for (i = 0; i < _max_iter_count; i++){
            var _hit = project.hitTest(point, {
                fill: true, 
                stroke: true, 
                segments: true, 
                tolerance: tolerance,
                match: function(hit){
                    if (hit.item && hit.item.data && hit.item.data.type === "pointer"){
                        return false
                    }
                    return true
                }
            })
    
            if(_hit){
                hit = _hit;
                // hit has happened between prev and curr points 
                // step half way backward
                point -= _step 
                if (_step.length < _error){
                    // step is too small, stop trials as soon 
                    // as no hit can be found
                    _stopSearching = true;
                }
            } else {
                if(firstPass || _stopSearching){
                    break;
                } else  {
                    // not hit found, but we should 
                    // step forward to search for a more 
                    // accurate collision point 
                    point += _step  
                }
            }
            firstPass = false;
            if(_step.length >= _error * 2 ) {
                _step /= 2
            } else {
                // minimum step length must be error/2
                // in order to save loop count 
                _step = _step.normalize(_error * 0.8)
            }
        }
        if (i > _acceptable_iter_count){
            console.log("found at " + i + ". iteration, step: ", _step.length)
        }
        return {point, hit}
    }
    

    算法

    1. 执行命中测试。
    2. 如果发现命中,则考虑实际碰撞可能发生在先前位置和当前位置之间的任何位置上。 delta ).
    3. 退后一步 delta = delta / 2 做一个命中测试。
    4. 如果仍然命中,重复步骤3。
    5. δ=δ/2
    6. 如果仍然没有命中,重复步骤5。
    7. 如果命中,如果步骤8允许,重复步骤3。
    8. 如果步长( 三角洲 )太小或进程超过最大迭代限制,请中断并返回可能的最佳冲突点(提供最佳冲突点的项的点)

    进一步改进

    此解决方案基于以下问题:检测命中是 还不够 ,因为检测到碰撞时可能太晚了,实际碰撞可能发生在上一个点和当前点之间的任何位置(在 三角洲 )由于命中检测间隔。

    这种试错法可以通过在正确计算的基础上开始非常精确的“猜测”来改进 在发现第一次撞击后 .

    我们可以计算 1 2 . 好像是最近的一点 3 4 :

    enter image description here

    如果计算正确,那么只有2个循环就足以检测碰撞点:第一个循环用于检测碰撞(如所示) #2 )第二个是验证。如果它不正确,试错法将接管。

        3
  •  0
  •   nicholaswmin    6 年前

    nearest point 在从移动点的位置开始的路径上。

    如果点与最近点之间的距离低于阈值(等于 strokeWidth Path

    不过有个陷阱 :的 strokeJoin strokeCap 路径的属性应该是 设置为 round ,因为这会导致左右对称的笔划宽度

    举个例子:

    var path = new Path({
        segments: [
            new Point(100, 200),
            new Point(200, 100),
            new Point(300, 300)
        ],
        strokeWidth: 100,
        // This is important.
        strokeJoin: 'round',
        // This is important.
        strokeCap: 'round',
        strokeColor: 'red',
        selected: true,
        position: view.center
    })
    
    var dot = new Path.Circle({
        center: view.center,
        radius: 10,
        fillColor: 'red'
    })
    
    function onMouseMove(event) {
        dot.position = event.point
    
        var nearestPoint = path.getNearestPoint(dot.position)
        var distance = dot.position.subtract(nearestPoint)
    
        if (distance.length < (dot.bounds.width / 2) + (path.strokeWidth / 2)) {
            path.strokeColor = 'blue'
        } else {
            path.strokeColor = 'red'
        }
    }
    

    以及 Sketch .


    也就是说,最好的方法就是等待(或实现自己), Path dilation/offsetting 笔划连接 / 冲程盖 设置。

    理想情况下,扩张/抵消将用相同的方法完成 笔划连接 / 冲程盖 的属性 路径

    有一个 JS library that reliably does polygon offsetting 可能对你有用。