如何在strokeBounds之间执行碰撞检测

时间:2018-10-26 23:45:56

标签: paperjs

这是我意图的简化形式:Sketch

我只希望当橙色圆圈离该行足够近时(当灰色圆圈触摸时),警报框变为红色:

enter image description here

但是,当前它可能会引发错误警报,即。在足够接近之前:

enter image description here

我们如何正确检测碰撞?

3 个答案:

答案 0 :(得分:1)

答案 1 :(得分:1)

我想出了“反复试验”的方法: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. 如果找不到匹配项,请前进delta = delta / 2
  6. 如果仍然没有命中,请重复步骤5。
  7. 如果命中,请在步骤8允许的情况下重复步骤3。
  8. 如果步长(delta)太小或过程超出了最大迭代限制,请中断并返回可能的最佳碰撞点(该项目提供最佳碰撞点的点)

进一步的改进

此解决方案基于以下问题:检测不到一个碰撞,因为检测到碰撞可能为时已晚,并且在先前点与当前点之间的任何地方都可能发生实际碰撞( (delta上的点击次数)。

可以通过在检测到第一个击中之后基于适当的计算进行非常准确的“猜测”的尝试来改进这种反复试验的方法。

我们可以使用1一杆计算2(实际碰撞点)。似乎是34的最近点:

enter image description here

如果计算正确,则仅2个循环就足以检测到碰撞点:第一个用于检测碰撞(如#2所示),第二个用于验证。如果不正确,将采用试错法。

答案 2 :(得分:0)

您可以尝试从移动点的位置开始在路径上找到nearest point。而不是尝试查找直接相交。

如果点和最近点之间的距离下降到阈值以下(等于strokeWidth的{​​{1}}和点的半径),则可以认为它是有效命中。

有一个问题:您路径的strokeJoinstrokeCap属性应为 设置为Path,因为这会导致围绕的对称笔划宽度 拐角和结尾,因此避免了假阴性。

这是一个例子:

round

Sketch


话虽如此,最好的方法是等待{或执行自己)Path dilation/offsetting并在膨胀路径上找到交点,因为这将允许您使用任何{{1 }} / 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' } } 设置。

理想情况下,将使用与strokeJoin相同的strokeCap / strokeJoin属性进行膨胀/抵消。

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