Javascript 检查矢量/角度是否与区域相交

Javascript 检查矢量/角度是否与区域相交,javascript,canvas,Javascript,Canvas,我想做的是: 我有两个x,y点,p1和p2,以及以弧度表示的p1角度的旋转值。 P2还有另外两个变量,一个宽度和高度,我称之为p2w和p2h。我想检查p1的角度是否与p2的边界相交,半径为宽度和/或高度 换句话说,如果角度穿过中心p2、宽度p2w和高度p2h的平方 以下是一个图表,以便于更好地理解: 我一直想做的是: if( p1.rot > (Math.atan2(p2.y-p2h, p2.x-p2w)) && p1.rot < (Math.atan2(p2.y

我想做的是: 我有两个x,y点,p1和p2,以及以弧度表示的p1角度的旋转值。 P2还有另外两个变量,一个宽度和高度,我称之为p2w和p2h。我想检查p1的角度是否与p2的边界相交,半径为宽度和/或高度

换句话说,如果角度穿过中心p2、宽度p2w和高度p2h的平方

以下是一个图表,以便于更好地理解:

我一直想做的是:

if( p1.rot > (Math.atan2(p2.y-p2h, p2.x-p2w)) 
&& p1.rot < (Math.atan2(p2.y+p2h, p2.x+p2w)) )
//There's an intersection
但正如你所猜测的,它并没有按预期的那样工作;还有其他方法吗?

这里有一种方法可以测试线段是否为光线?和矩形相交

只需测试线段/光线是否与矩形的两条对角线相交

示例代码和演示:

var canvas=document.getElementByIdcanvas; var ctx=canvas.getContext2d; var cw=画布宽度; var ch=画布高度; ctx.线宽=3; 函数重偏移{ var BB=canvas.getBoundingClientRect; offsetX=BB.left; offsetY=BB.top; } var offsetX,offsetY; 再抵消; window.onscroll=function{reOffset;} var ray1={x:30,y:250,角度:-Math.PI/3.5}; var ray2={x:30,y:250,角度:-Math.PI/6}; var r={x:100,y:100,w:40,h:40}; ctx.strokeStyle='black'; ctx.strokeRectr.x,r.y,r.w,r.h; //此光线与矩形相交 Drawray1,r; //此光线与矩形不相交 Drawray2,r; 函数drawRayray,rect{ var intersects=光线矩形相交光线,矩形; ctx.beginPath; ctx.moveToray.x,ray.y; ctx.lineToray.x+1000*数学.cosray.angle,ray.y+1000*数学.sinray.angle; ctx.strokeStyle=相交?'red':'green'; ctx.stroke; } 函数rayrectray,rect{ d0={x:rect.x,y:rect.y}; d1={x:rect.x+rect.w,y:rect.y+rect.h}; d2={x:rect.x,y:rect.y+rect.h}; d3={x:rect.x+rect.w,y:rect.y}; ray0={x:ray.x,y:ray.y}; ray1={x:ray.x+1000*Math.cosray.angle,y:ray.y+1000*Math.sinray.angle}; var diag1Test=line2lineIntersectionray0、ray1、d0、d1; var diag2Test=line2lineIntersectionray0、ray1、d2、d3; 返回诊断1测试| |诊断2测试; } //获取两条线段的中间点(如果有) //归属:http://paulbourke.net/geometry/pointlineplane/ 功能线2直线相交P0、p1、p2、p3{ var unknownA=p3.x-p2.x*p0.y-p2.y-p3.y-p2.y*p0.x-p2.x; var unknownB=p1.x-p0.x*p0.y-p2.y-p1.y-p0.y*p0.x-p2.x; var分母=p3.y-p2.y*p1.x-p0.x-p3.x-p2.x*p1.y-p0.y; //测试是否一致 //如果ua和ub的分母和分子为0 //那么这两条线是重合的。 ifunknownA==0&&unknownB==0&&denominator==0{returntrue;} //测试是否并行 //如果ua和ub方程的分母为0 //那么这两条线是平行的。 如果分母==0,则返回false; //如果需要线段的交点 //然后只需要测试ua和ub是否介于0和1之间。 //无论哪一个在该范围内,则对应的 //线段包含交点。 //如果两者都在0到1的范围内,则 //交点位于两条线段内。 未知数a/=分母; 未知nb/=分母; var isIntersecting=unknownA>=0&&unknownA=0&&unknownB这里有一种方法可以测试线段光线和矩形是否相交

只需测试线段/光线是否与矩形的两条对角线相交

示例代码和演示:

var canvas=document.getElementByIdcanvas; var ctx=canvas.getContext2d; var cw=画布宽度; var ch=画布高度; ctx.线宽=3; 函数重偏移{ var BB=canvas.getBoundingClientRect; offsetX=BB.left; offsetY=BB.top; } var offsetX,offsetY; 再抵消; window.onscroll=function{reOffset;} var ray1={x:30,y:250,角度:-Math.PI/3.5}; var ray2={x:30,y:250,角度:-Math.PI/6}; var r={x:100,y:100,w:40,h:40}; ctx.strokeStyle='black'; ctx.strokeRectr.x,r.y,r.w,r.h; //此光线与矩形相交 Drawray1,r; //此光线与矩形不相交 Drawray2,r; 函数drawRayray,rect{ var intersects=光线矩形相交光线,矩形; ctx.beginPath; ctx.moveToray.x,ray.y; ctx.lineToray.x+1000*数学.cosray.angle,ray.y+1000*数学.sinray.angle; ctx.strokeStyle=相交?'red':'green'; ctx.stroke; } 函数rayrectray,rect{ d0={x:rect.x,y:rect.y}; d1={x:rect.x+rect.w,y:rect.y+rect.h}; d2={x:rect.x,y:rect.y+rect.h}; d3={x:rect.x+rect.w,y:rect.y}; ray0={x:ray.x,y:ray.y}; ray1={x:ray.x+1000*Math.cosray.angle,y:ray.y+1000*Math.sinray.angle}; var diag1Test=line2lineIntersectionray0、ray1、d0、d1; var diag2Test=line2lineIntersectionray0、ray1、d2、d3; 返回诊断1测试| |诊断2测试; } //获取两条线段的中间点(如果有) //归属:http://paulbourke.net/geometry/pointlineplane/ 功能线2直线相交P0、p1、p2、p3{ var unknownA=p3.x-p2.x*p0.y-p2.y-p3.y-p2.y*p0.x-p2.x; var unknownB=p1.x-p0.x*p0.y-p2.y-p1.y-p0.y*p0.x-p2.x; 德诺明 ator=p3.y-p2.y*p1.x-p0.x-p3.x-p2.x*p1.y-p0.y; //测试是否一致 //如果ua和ub的分母和分子为0 //那么这两条线是重合的。 ifunknownA==0&&unknownB==0&&denominator==0{returntrue;} //测试是否并行 //如果ua和ub方程的分母为0 //那么这两条线是平行的。 如果分母==0,则返回false; //如果需要线段的交点 //然后只需要测试ua和ub是否介于0和1之间。 //无论哪一个在该范围内,则对应的 //线段包含交点。 //如果两者都在0到1的范围内,则 //交点位于两条线段内。 未知数a/=分母; 未知nb/=分母;
var isIntersecting=unknownA>=0&&unknownA=0&&unknownB船长,请救命

您正在询问光线是否与矩形相交。这是我们需要做的

首先,使用点和向量或点和角度定义光线。因为使用向量更容易,所以让我们将角度转换为向量。使用勾股定理,角度φ与向量n={x:Math.cosphi,y:Math.sinphi}相同

我将重命名您的变量,以使标记更容易。我将用p表示p1,用r表示隐式定义的矩形

现在,对于射线上的任意随机点M,下列方程必须成立:

M.x = p.x + n.x * alpha (1)
M.y = p.y + n.y * alpha
M.x >= r.x
M.x <= r.x + r.w
M.y >= r.y
M.y <= r.y + r.h
为了一些真正的阿尔法。类似地,对于矩形内的任意随机点M,以下不等式必须成立:

M.x = p.x + n.x * alpha (1)
M.y = p.y + n.y * alpha
M.x >= r.x
M.x <= r.x + r.w
M.y >= r.y
M.y <= r.y + r.h
对于同时位于光线上和矩形内的点,方程和不等式都必须成立。替换上述值,我们得到:

p.x + n.x * alpha >= r.x
p.x + n.x * alpha <= r.x + r.w
p.y + n.y * alpha >= r.y
p.y + n.y * alpha <= r.y + r.h
求解alpha,我们得到:

alpha >= (r.x - p.x) / n.x
alpha <= (r.x + r.w - p.x) / n.x
alpha >= (r.y - p.y) / n.y
alpha <= (r.y + r.h - p.y) / n.y
当且仅当满足以下条件时,上述系统才有解决方案:

var lowerLimitX = (r.x - p.x) / n.x;
var lowerLimitY = (r.y - p.y) / n.y;
var upperLimitX = (r.x + r.w - p.x) / n.x;
var upperLimitY = (r.y + r.h - p.y) / n.y;
var minAlpha = Math.max(lowerLimitX, lowerLimitY);
var maxAlpha = Math.min(upperLimitX, upperLimitY);
var hasSolution = minAlpha<= maxAlpha;
现在,如果上面的系统有一个解决方案,那么必须至少有一个点位于光线和矩形上,换句话说,它们相交

编辑:这里有一个例子。移动鼠标以查看结果。注意,由于Y轴在HTML画布API中向下生长,因此必须交换Y轴的下限和上限


编辑2:如果您关心@pfannkuchen_gesicht建议的交点,请注意,通常,交点将是线段,而不是点,这也很简单。我们已经知道,对于交点上的点,光线方程必须成立。要查找点本身,只需用1中[minAlpha;maxAlpha]范围内的值替换alpha即可。例如,最近的点是p+minAlpha*n,最远的点是p+maxAlpha*n,中间的一个随机点是p+minAlpha+Math.random*maxAlpha-minAlpha*n.

数学队长救命

您正在询问光线是否与矩形相交。这是我们需要做的

首先,使用点和向量或点和角度定义光线。因为使用向量更容易,所以让我们将角度转换为向量。使用勾股定理,角度φ与向量n={x:Math.cosphi,y:Math.sinphi}相同

我将重命名您的变量,以使标记更容易。我将用p表示p1,用r表示隐式定义的矩形

现在,对于射线上的任意随机点M,下列方程必须成立:

M.x = p.x + n.x * alpha (1)
M.y = p.y + n.y * alpha
M.x >= r.x
M.x <= r.x + r.w
M.y >= r.y
M.y <= r.y + r.h
为了一些真正的阿尔法。类似地,对于矩形内的任意随机点M,以下不等式必须成立:

M.x = p.x + n.x * alpha (1)
M.y = p.y + n.y * alpha
M.x >= r.x
M.x <= r.x + r.w
M.y >= r.y
M.y <= r.y + r.h
对于同时位于光线上和矩形内的点,方程和不等式都必须成立。替换上述值,我们得到:

p.x + n.x * alpha >= r.x
p.x + n.x * alpha <= r.x + r.w
p.y + n.y * alpha >= r.y
p.y + n.y * alpha <= r.y + r.h
求解alpha,我们得到:

alpha >= (r.x - p.x) / n.x
alpha <= (r.x + r.w - p.x) / n.x
alpha >= (r.y - p.y) / n.y
alpha <= (r.y + r.h - p.y) / n.y
当且仅当满足以下条件时,上述系统才有解决方案:

var lowerLimitX = (r.x - p.x) / n.x;
var lowerLimitY = (r.y - p.y) / n.y;
var upperLimitX = (r.x + r.w - p.x) / n.x;
var upperLimitY = (r.y + r.h - p.y) / n.y;
var minAlpha = Math.max(lowerLimitX, lowerLimitY);
var maxAlpha = Math.min(upperLimitX, upperLimitY);
var hasSolution = minAlpha<= maxAlpha;
现在,如果上面的系统有一个解决方案,那么必须至少有一个点位于光线和矩形上,换句话说,它们相交

编辑:这里有一个例子。移动鼠标以查看结果。注意,由于Y轴在HTML画布API中向下生长,因此必须交换Y轴的下限和上限


编辑2:如果您关心@pfannkuchen_gesicht建议的交点,请注意,通常,交点将是线段,而不是点,这也很简单。我们已经知道,对于交点上的点,光线方程必须成立。要查找点本身,只需用1中[minAlpha;maxAlpha]范围内的值替换alpha即可。例如,最近的点是p+minAlpha*n,最远的点是p+maxAlpha*n,中间的一个随机点是p+minAlpha+Math.random*maxAlpha-minAlpha*n。

非常好的方法!我唯一缺少的是一个交叉点OP并没有要求这样做,但对于一些应用程序来说可能会很有趣,但我认为这是一个很好的快速预测试方法。这是一个很好的解释,t
谢谢你!我会看看这对我的案子是否有效,但你是最好的答案anyway@pfannkuchen_gesicht请参见编辑2.0嘿,最近我再次查看了您的答案,想知道是否也可以在3D中使用此选项。但显然它不起作用。你知道3D AABBs是否有比全面测试更好的射线测试方法吗?因此,在仔细考虑并询问他人后,问题现在其实相当明显。所以它可以归结为不等式和与负数相乘,从而改变它,从而使上界变为下界。所以本质上你必须根据n.{x,y,z}符号交换边界。此外,您还必须引入一个检查,检查最小/最大alpha是否为正,然后才是有效的解决方案。看:真的很好的方法!我唯一缺少的是一个交叉点OP没有要求这样做,但是对于一些应用程序来说会很有趣,但是我认为这是一个很好的快速预测试方法。这是一个很好的解释,谢谢!我会看看这对我的案子是否有效,但你是最好的答案anyway@pfannkuchen_gesicht请参见编辑2.0嘿,最近我再次查看了您的答案,想知道是否也可以在3D中使用此选项。但显然它不起作用。你知道3D AABBs是否有比全面测试更好的射线测试方法吗?因此,在仔细考虑并询问他人后,问题现在其实相当明显。所以它可以归结为不等式和与负数相乘,从而改变它,从而使上界变为下界。所以本质上你必须根据n.{x,y,z}符号交换边界。此外,您还必须引入一个检查,检查最小/最大alpha是否为正,然后才是有效的解决方案。见: