在Canvas中检测鼠标与闭合Bezier曲线形状的碰撞

Detecting mouse collision with closed Bezier curved shapes in Canvas

本文关键字:曲线 Bezier 碰撞 Canvas 检测 鼠标      更新时间:2023-11-27

我的情况:

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

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

(1) 一种方法是把画布上的所有东西都画两次。第一次,每个形状都用哈希表中的唯一颜色填充。第二次,这些形状在第一层上获得了真实的颜色和遮罩。为了检测鼠标形状碰撞,我必须从第一层获取鼠标下像素的颜色,并将获得的颜色映射到哈希表中的相应形状。

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

实际问题:

我不喜欢第一种方法,因为所有的东西都必须画两次,这在计算上并不便宜。但是,由于计算中的舍入误差,第二种方法不能保证准确性。

理想情况下,我希望将第二种方法的准确性提高到近乎完美。

我试图提高精度的方法是向不同的方向投射4条光线:顶部、左侧、底部和右侧。如果至少有一条水平和一条垂直射线表明鼠标在形状内,那么我得出结论,该点在形状内。尽管这样可以消除大多数不匹配,但当鼠标位于形状内部时,仍会发生错误(而不是激发)。

如果有人能建议修复光线投射算法,或者甚至第三种选择,那将是一件很棒的事情!

提前谢谢。

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

var flag = ctx.isPointInPath(x, y);

你需要做的只是重建你想要测试的每个路径(不需要笔划或填充它们)并进行这个测试。

还有:

var flag = ctx.isPointInStroke(x, y);

如果您还想在笔划宽度>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);

你需要为每个独特的形状做这件事,但除非你有无数的形状,否则性能是可以的,在这种情况下,你可以考虑四叉树之类的。

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