java - 具有公差的贝塞尔曲线命中检测?

标签 java javascript gwt canvas

我在 Canvas 中有一些二次贝塞尔曲线。如果它们只有 1-2 像素宽,我怎样才能最好地对它们进行 hit detection,并且我想提供某种容差,这样用户就不必准确地点击行。

有没有什么东西可以计算贝塞尔曲线的最小距离,如果距离足够小,就选择贝塞尔曲线?

最佳答案

我至少可以想到 3 种方法来扩大二次贝塞尔曲线的命中区域

我不推荐第一个解决方案,但无论如何它都在这里!

解决方案#1——针对贝塞尔曲线上计算的各个点手动测试您的 clickPoint

这是一个用于计算 XY 的函数,它是沿着贝塞尔曲线的 n%,还有一个函数用于测试您的 clickPoint 是否在该贝塞尔曲线点的范围内。

var startPt=makePt(10,100);
var controlPt=makePt(50,30);
var endPt=makePt(90,100);

function makePt(X,Y){ return( { x:X, y:Y } ) }

// find points at various percent along bezier path
// (where percent is a decimal from 0 to 1)
function getQuadraticBezierXY(percent,startPt,controlPt,endPt) {
    var x = Math.pow(1-percent,2) * startPt.x + 2 * (1-percent) * percent * controlPt.x + Math.pow(percent,2) * endPt.x; 
    var y = Math.pow(1-percent,2) * startPt.y + 2 * (1-percent) * percent * controlPt.y + Math.pow(percent,2) * endPt.y; 
    return( makePt(x,y) );
}

// find whether 2 points are close to each other
// range is your pixel tolerance
function arePointsInRange(bezPt,testPt,range){
    var dx=testPt.x-bezPt.x;
    var dy=testPt.y-bezPt.y;
    return( dx*dx+dy*dy <= range*range )
}

解决方案#2—针对“加宽”曲线的封闭路径进行 HitTest

注意:下面使用的 isPointInPath() 在现代浏览器上可用,但在旧版浏览器上不可用

注意:您不必实际向用户显示加宽的曲线——您可以绘制加宽的曲线,但不能绘制 context.stroke()。 (一定要查看 isPointInPath 的文档)。

注意:请务必根据起点和终点之间的直线斜率调整偏移量。为简单起见,我在下面的插图中使用了 0 斜率。

这是代码和 fiddle :http://jsfiddle.net/m1erickson/4GEeu/

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    ctx.lineWidth=2;
    ctx.strokeStyle="red";


    var startX=10;
    var startY=100;
    var controlX=50;
    var controlY=50;
    var endX=90;
    var endY=100;
    var offset=20;

    ctx.beginPath();
    ctx.moveTo(startX,startY-offset);
    ctx.quadraticCurveTo(controlX,controlY-offset,endX,endY-offset);
    ctx.lineTo(endX,endY+offset);
    ctx.quadraticCurveTo(controlX,controlY+offset,startX,startY+offset);
    ctx.closePath();
    ctx.stroke();

    // hitTest point [15,110] which is known to be inside
    // the widened curve path
    if(ctx.isPointInPath(15,110)){
        alert("Point [15,110] is in the closed quadratic curve path");
    }

解决方案#3—针对屏幕外 Canvas 上加宽的曲线进行 HitTest

注意:我的插图只是将屏幕上的曲线拉得更宽。您可以在屏幕外 Canvas 上进行测试。

这是代码和 fiddle :http://jsfiddle.net/m1erickson/MJfZt/

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    ctx.lineWidth=20;
    ctx.strokeStyle="red";

    var startX=10;
    var startY=100;
    var controlX=50;
    var controlY=50;
    var endX=90;
    var endY=100;
    var offset=20;

    ctx.beginPath();
    ctx.moveTo(startX,startY);
    ctx.quadraticCurveTo(controlX,controlY,endX,endY);
    ctx.stroke();

    // hitTest point [10,100] which is known to be inside
    // the widened curve path
    if(hittestByColor(10,100,255,0,0)){
          alert("Pixel [10,100] is inside the widened curve");       
    }

    function hittestByColor(x,y,red,green,blue){
        var pxData = ctx.getImageData(x,y,1,1);
        return(pxData.data[0]==red 
            && pxData.data[1]==green 
            && pxData.data[2]==blue);
    }

关于java - 具有公差的贝塞尔曲线命中检测?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15987966/

相关文章:

java - 使用Java格式将rtf转换为html

java - 计算开始时间和耗时的时间差

java - 构建器模式,具有公共(public)构造函数有效

javascript - 如何知道哪个提交按钮触发了 onsubmit 事件

gwt - 如何将 Google Identity Toolkit 与单个网页应用程序(例如 GWT)集成

java - 从java项目中提取所有字符串

java - 如何使用 JAXB 使基类字段成为子类 xml 模式的属性

javascript - 不允许加载本地资源 : file

javascript - 触发 iframe 下两级内的链接

java - 列表中的 GXT 模板事件句柄