在Canvas中使用封闭的Bezier曲线形状检测鼠标碰撞

Han*_*ans 1 javascript algorithm html5 bezier canvas

我的情况:

我正在研究用于缩放用户界面的HTML5/Canvas/JavaScript框架,然后我将其用于数据可视化Web应用程序项目.我的框架需要的一个功能是能够检测用户的鼠标是否在渲染的形状上.对于更复杂的形状,如多边形和带Bezier曲线的形状,这将成为一项挑战.

我找到了解决这个问题的两种方法:

(1)一种方法是在画布上画两次.第一次,每个形状都填充了哈希表中的独特颜色.第二次,形状在第一层上获得真实的颜色和面具.为了检测鼠标形状的碰撞,我必须从第一层抓取鼠标下的像素颜色,并将我得到的颜色映射到散列表中的相应形状.

(2)或者我可以使用光线投射算法(http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm).我实际上已经使用检测射线冲突和射线贝塞尔碰撞的代码实现了这个算法.

实际问题:

我不喜欢第一种方法,因为一切都必须被绘制两次,这在计算上并不便宜.但是由于计算的舍入误差,第二种方法不能保证准确性.

理想情况下,我希望提高第二种方法接近完美的准确性.

我提高准确度的尝试是在不同方向投射4条光线:顶部,左侧,底部和右侧.如果至少有一条水平光线和一条垂直光线表明鼠标在形状内部,那么我认为该点位于形状内部.虽然这样可以消除大多数失火,但当鼠标位于形状内部时,仍然会发生错误(不会发射).

如果有人可以建议修复光线投射算法,或者甚至是第三种选择,那将是非常棒的!

提前致谢.

小智 7

您可以进行光线投射,但您也可以在上下文中使用内置函数:

var flag = ctx.isPointInPath(x, y);
Run Code Online (Sandbox Code Playgroud)

您需要做的只是重建您想要测试的每个路径(不需要描边或填充它们)并进行此测试.

还有:

var flag = ctx.isPointInStroke(x, y);
Run Code Online (Sandbox Code Playgroud)

如果您还想考虑笔划本身,以防它的宽度> 1.尽管如此,IE上不支持此功能.

例如:

/// build some polygon/shape/...
ctx.beginPath();;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();

/// no need to fill/stroke it, just test the path:
var flag = ctx.isPointInPath(x, y);
Run Code Online (Sandbox Code Playgroud)

你需要为每个独特的形状做这个,但性能是相当不错的,除非你有zillion形状,在这种情况下你可以考虑四叉树等.

要检测单击的形状,可以将形状存储为对象而不是使用唯一的颜色(虽然取决于实际情况),因此当您遍历对象数组时,您将知道当前正在检查哪个对象,如果是真的则中止迭代.