所以,我有一个HTML画布,我可以在上面绘制东西。
我想做的是制作一个javascript函数来告诉我一条线是否与某个点发生碰撞。
我见过算法来测试一条线是否与另一条线发生碰撞,如下所示:
var IsIntersecting = function(Point a, Point b, Point c, Point d)
{
var denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X));
var numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y));
var numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y));
if (denominator == 0) { return numerator1 == 0 && numerator2 == 0;}
var r = numerator1 / denominator;
var s = numerator2 / denominator;
return (r >= 0 && r <= 1) && (s >= 0 && s <= 1);
}
但我想知道它是否与特定点发生碰撞。我还没有找到一个好的算法来做到这一点。你能救我吗?
答案 0 :(得分:1)
a
和b
是指向p
之间的行的端点。
假设,这应该有效:
function pointOnLine(a, b, p) {
var lx = b.x - a.x
var ly = b.y - a.y
var dx = p.x - a.x
var dy = p.y - a.y
var l = Math.sqrt(lx * lx, ly * ly)
var d = Math.sqrt(dx * dx, dy * dy)
var q = d / l
return d <= l && q * lx === dx && q * ly === dy
}
但是,由于浮点错误,并且考虑到您可能正在使用像素坐标,我认为这样就可以使用.5
的默认错误:
function pointOnLine(a, b, p) {
var e = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : .5;
var lx = b.x - a.x
var ly = b.y - a.y
var dx = p.x - a.x
var dy = p.y - a.y
var l = Math.sqrt(lx * lx, ly * ly)
var d = Math.sqrt(dx * dx, dy * dy)
var q = d / l
return d <= l && Math.abs(q * lx - dx) < e && Math.abs(q * ly - dy) < e
}
这是一种类似的方法,可以解决Math.sqrt()
为绝对值大于Infinity
的组件返回Math.sqrt(Number.MAX_VALUE)
时的边缘问题。它使用一种名为Math.hypot()
的新方法:
function pointOnLine({ x: ax, y: ay }, { x: bx, y: by }, { x: px, y: py }, e = .5) {
const lx = bx - ax
const ly = by - ay
const dx = px - ax
const dy = py - ay
const l = Math.hypot(lx, ly)
const d = Math.hypot(dx, dy)
const q = d / l
return d <= l && Math.abs(q * lx - dx) < e && Math.abs(q * ly - dy) < e
}
此方法获取行lx
,ly
的端点之间的组件差异,然后计算行l
的长度。
然后获取行p
,a
的测试点dx
和点dy
之间的组件差异,并计算这些点之间的距离{{1 }}
之后,它计算d
的商q
,并且测试首先检查距离d / l
是否小于d
长度,以确保它可能是l
指向该行的端点之间。最后,它检查线端点之间的组件差异乘以商q
是否等于点p
和a
之间的组件差异。
如果是,则确定点p
位于端点a
和b
之间的线上。
答案 1 :(得分:1)
这是另一种查找点是否在线附近的方法。
由于浮点数具有有限的精度,因此找出一个点是否在一条线上是不可能完成的,并且这引入的误差意味着理想结果calcs === 0
将失败很多次,即使是最小的错误1e-15(小于原子)所以我们称函数为isPointNearLine
而不是isPointOnLine
function isPointNearLine(a,b,p){
const v1 = { x : b.x - a.x, y : b.y - a.y };
const l2 = v1.x * v1.x + v1.y * v1.y;
if(l2=== 0){ return false } // line has no length so can't be near anything
const v2 = { x : p.x - a.x, y : p.y - a.y };
const u = (v1.x * v2.x + v1.y * v2.y) / l2;
return u >= 0 && u <= 1 && Math.abs((v1.x * v2.y - v1.y * v2.x) / Math.sqrt(l2)) < 1;
}
作为游戏程序员,性能始终是任何算法中最重要的部分。上述功能是一种通用的性能解决方案。在Firefox上平稳,该功能可以每秒计算28万亿个解决方案。当你将它与Patrick Roberts进行比较时,答案明显更快。 Patrick Robert的ES6解决方案每秒只能获得0.98亿个解决方案。
但公平地说,他的解决方案并没有考虑到绩效。快速重写
// don't use destructuring as it is presently very very slow
function pointOnLine(a, b, p) {
const lx = b.x - a.x
const ly = b.y - a.y
const dx = p.x - a.x
const dy = p.y - a.y
const l = Math.sqrt(lx * lx + ly * ly) // don't use hypot as it is 5 times slower
const d = Math.sqrt(dx * dx + dy * dy) // than using sqrt and the 2 multiplications and one addition
const q = d / l
return d <= l && Math.abs(q * lx - dx) < 0.5 && Math.abs(q * ly - dy) < 0.5
}
你得到一个&gt;每秒24万亿个解决方案的性能提升2400%。
虽然现在他的功能有一个优点,因为他使用Numbers
而不是Objects
来存储中间结果,而他的代码忽略了零长度线(我总是认为线长是更高的水平功能,永远不应该是低级功能的问题)所以重写我的代码
function isPointNearLine(a,b,p){
const lx = b.x - a.x;
const ly = b.y - a.y;
const l2 = lx * lx + ly * ly;
const dx = p.x - a.x;
const dy = p.y - a.y;
const u = (lx * dx + ly * dy) / l2;
return u >= 0 && u <= 1 && Math.abs((lx * dy - ly * dx) / Math.sqrt(l2)) < 1;
}
现在每秒处理35.7万个解决方案。
我的功能比他快得多的主要原因是他的代码中有2个额外的函数调用。他的功能需要至少拨打2次Math.sqrt
,最多2次sqrt
和2次abs
来电。我的函数可以找到没有任何函数调用的解决方案,最坏的情况是abs
和sqrt
虽然我的功能可能看起来更多,但是避免函数调用值得额外的操作。
基准使用3个随机点
A.x = Math.random() * 1000 - 500;
A.y = Math.random() * 1000 - 500;
B.x = Math.random() * 1000 - 500;
B.y = Math.random() * 1000 - 500;
P.x = Math.random() * 1000 - 500;
P.y = Math.random() * 1000 - 500;
仅定时功能
// time start
isPointNearLine(A,B,P);
// time end
在
上运行