Javascript 我怎样才能解这个三角问题
我正在创建一个游戏,并希望解决这个问题,使我的游戏功能 AsinRX+BcosRX+CX+D=0 找到最小X值,该值等于或大于0,小于或等于1。 A、 B、C和R是变量 我可以使用反三角函数,如acos、asin等。但我想让计算尽可能准确 我试着用三角函数公式使它变得简单,但我做不到 更详细地说,, 有一个带移动和碰撞的旋转框Javascript 我怎样才能解这个三角问题,javascript,collision-detection,game-physics,trigonometry,Javascript,Collision Detection,Game Physics,Trigonometry,我正在创建一个游戏,并希望解决这个问题,使我的游戏功能 AsinRX+BcosRX+CX+D=0 找到最小X值,该值等于或大于0,小于或等于1。 A、 B、C和R是变量 我可以使用反三角函数,如acos、asin等。但我想让计算尽可能准确 我试着用三角函数公式使它变得简单,但我做不到 更详细地说,, 有一个带移动和碰撞的旋转框 const Box = { Position: { x: 0, y: -300 }, Velocity: { x: 1500,
const Box = {
Position: {
x: 0,
y: -300
},
Velocity: {
x: 1500,
y: 500
},
RotationSpeed: 19,
DiagonalLength: 120
};
const Collision = {
p1: {
x: 50,
y: -200
},
p2: {
x: 1500,
y: 300
}
};
// **********************************************************
// Get a corner position of the BoX at provided time
// CornerID: 0-3 identify the edge
// time: Time
// **********************************************************
getBoxCornerPosition(CornerID, time) {
let addRadian = CornerID * Math.PI / 2;
let posx =
Box.Position.x + // Base position of the Box
Box.Velocity.x * time + // Move distance of the Box
Box.DiagonalLength * Math.cos(Box.RotationSpeed * time + addRadian); // Edge position
let posy =
Box.Position.y +
Box.Velocity.y * time +
Box.DiagonalLength * Math.sin(Box.RotationSpeed * time + addRadian);
return [posx, posy];
}
我想知道确切的时间,什么时候拐角处发生了碰撞
const Box = {
Position: {
x: 0,
y: -300
},
Velocity: {
x: 1500,
y: 500
},
RotationSpeed: 19,
DiagonalLength: 120
};
const Collision = {
p1: {
x: 50,
y: -200
},
p2: {
x: 1500,
y: 300
}
};
// **********************************************************
// Get a corner position of the BoX at provided time
// CornerID: 0-3 identify the edge
// time: Time
// **********************************************************
getBoxCornerPosition(CornerID, time) {
let addRadian = CornerID * Math.PI / 2;
let posx =
Box.Position.x + // Base position of the Box
Box.Velocity.x * time + // Move distance of the Box
Box.DiagonalLength * Math.cos(Box.RotationSpeed * time + addRadian); // Edge position
let posy =
Box.Position.y +
Box.Velocity.y * time +
Box.DiagonalLength * Math.sin(Box.RotationSpeed * time + addRadian);
return [posx, posy];
}
我知道不可能没有答案,也不可能有多个答案。
我想知道边缘碰撞的最短时间,如果有答案的话。
谢谢。如果我理解正确,这个正方形在平面内移动,使其中心以恒定的速度沿直线移动,而正方形本身以恒定的角速度围绕其中心旋转。然后,红色角点的运动,实际上是正方形上几乎任何一点的运动,由如下等式描述:
x(t) = r0*cos(R*t) - r0*sin(R*t) + v1*t + x0
y(t) = r0*sin(R*t) + r0*cos(R*t) + v2*t + y0
红线由一个方程给出,如下所示:
a*x + b*y = c
a*(r0*cos(R*t) - r0*sin(R*t) + v1*t + x0) + b*(r0*sin(R*t) + r0*cos(R*t) + v2*t + y0) = c
因此,当满足以下条件时,正方形的红角与红线发生碰撞,红角位于红线上:
a*x(t) + b*y(t) = c
明确地写出来的是这样的:
a*x + b*y = c
a*(r0*cos(R*t) - r0*sin(R*t) + v1*t + x0) + b*(r0*sin(R*t) + r0*cos(R*t) + v2*t + y0) = c
通过将相似的术语组合在一起:
r0*(a + b)*cos(R*t) + r0*(b - a)*sin(R*t) + (a*v1 + b*v2)*t + (a*x0 + b*y0 - c) = 0
或以缩写形式:
A*cos(R*t) + B*sin(R*t) + C*t + D = 0
其中A=r0*A+b,b=r0*b-A,C=A*v1+b*v2,D=A*x0+b*y0-C
如果我们重命名参数t=X
我们精确地得到了你的方程式
A*cos(R*X) + B*sin(R*X) + C*X + D = 0
这个方程没有封闭形式的解,你必须用数值方法求解。为此,必须查看函数Y=fX,其中
该函数fX的几何意义是,ft是时间t处的红点与红线之间的定向距离乘以常数,更精确地说是1/sqrta^2+b^2。事实上,如果选择红线的系数a、b、c,使a^2+b^2=1,则ft正好是时间t处的红点与红线之间的定向距离
为了解决这个问题,首先我们将函数重写如下
A_B = sqrt(A^2 + B^2)
f(X) = A_B*( (A/A_B)*cos(R*X) + (B/A_B)*sin(R*X) ) + C*X + D
因为A/A_B^2+B/A_B^2=1,所以存在一个角度w,使得
cos(w) = A/A_B = A/sqrt(A^2 + B^2)
所以我们可以设定
w = arccos(A/A_B)
因此
f(X) = A_B*( cos(w)*cos(R*X) + sin(w)*sin(R*X) ) + C*X + D
= A_B*cos(R*X - w) + C*X + D
首先,你要看导数
f_prime(X) = - R*A_B*sin(R*X - w) + C
找到它的所有零,如果有的话,也就是所有的X,使得f_primeX=0
这仅在-1X = X - f(X)/f_prime(X)
直到absfXepsilon = 0.00001
X = X0
Y = f(X)
err = abs(Y)
while err > epsilon:
X = X - Y / f_prime(X)
Y = f(X)
err = abs(Y)
这就是我的想法,但是你必须计算出细节,修改我写的公式,确保没有错误 多亏了@Futurologist,我才能够编写正确运行的代码。 计算方法有些不同,但基本上我遵循了他建议我的方法 我添加了第一部分,检查是否命中。 谢谢
那么你已经有了解决方案了?请把它寄出去。但我想使计算尽可能准确到底什么不够准确?不,我不能。我可以画出我想要解决的情况。但我不知道如何得到我想要的答案。我意识到我的问题太模糊了,所以我补充了更多细节。谢谢。非常感谢你的详细建议!我会试试,我会在这里发布工作代码。@HirokiSatoyoshi对不起,我对我的帖子做了一些更新,我在公式中发现了一些错误。
// **********************************************************
// Get Box x, y according to given time
// This is for reference
// **********************************************************
getBoxPoint(time) {
let Mx = M.x;
let My = M.y;
let Px = P.x;
let Py = P.y;
let x = L * Math.cos(R * time) + Mx * time + Px;
let y = L * Math.sin(R * time) + My * time + Py;
return [x, y];
}
// **********************************************************
// return greater than 0 hit time [] in ascending order
// return null when it never hit
// **********************************************************
GetHitTimes() {
let result = checkParallel_HitRange();
if (result === null) {
console.log('Never Hit');
return null;
}
if (result === undefined) {
return calcHitTime(); // hit infinity times
}
let [minTime, maxTime] = result;
if (maxTime < 0) {
console.log('Never Hit in the future (hit in the past)');
return null;
}
if (minTime < 0) {
minTime = 0;
}
return calcHitTime(minTime, maxTime);
}
// **********************************************************
// Check if Box movement and Collision line is parallel
// Return:
// null: never hit
// undefined: hit positions are infinity
// [minTime, maxTime]: Range of potential collisions time
// **********************************************************
checkParallel_HitRange() {
if (a / b === -M.y / M.x) { // is Parallel
let distance = getColLineDistance(P.x, P.y);
if (distance > L) { // Box and Collision are never hit
return null;
}
// Collision points are infinity
return undefined;
}
else {
// Check range of potential collisions
// x = Mx * time + Px
// y = My * time + Py
// D = Math.sqrt(a * a + b * b)
// +-L = (a * x + b * y + c) / D
// +-L = ( (aMx + bMy) * time + aPx + aPy + c ) / D
// +-LD = (aMx + bMy) * time + aPx + aPy + c
// (aMx + bMy) * time = +-LD - aPx - aPy - c
// time = (+-LD - aPx - aPy -c) / (aMx + bMy)
let D = Math.sqrt(a * a + b * b);
let time1 = (L * D - c - a * P.x - b * P.y) / (a * M.x + b * M.y);
let time2 = (-L * D - c - a * P.x - b * P.y) / (a * M.x + b * M.y);
console.log('Potential collison times are ' + time1 + ' - ' + time2);
if (time1 < time2) {
return [time1, time2];
}
return [time2, time1];
}
}
// **********************************************************
// Get distance between given point from Collision line
// **********************************************************
getColLineDistance(x, y) {
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
}
// **********************************************************
// Calculate and return hit time array with in given min-max time
// **********************************************************
calcHitTime(minTime, maxTime) {
console.log('Min / Max ' + minTime + ', ' + maxTime);
let Mx = M.x;
let My = M.y;
let Px = P.x;
let Py = P.y;
// line: ax + by + c = 0
// x = L * Math.cos(R * time) + Mx * time + Px;
// y = L * Math.sin(R * time) + My * time + Py;
// a ( L * cos(R * time) + Mx * time + Px ) + b ( L * sin(R * time) + My * time + Py ) + c = 0
// aL * cos(R * time) + aMx * time + aPx + bL * sin(R * time) + bMy * time + bPy + c = 0
// aL * cos(R * time) + bL * sin(R * time) + aMx * time + bMy * time + aPx + bPy + c = 0
// bL * sin (R * time) + aL *cos(R * time) + (aMX + bMY) * time + Pxa + Pyb + c = 0;
// time = x / R
// bL * sin (x) + aL *cos(x) + (aMX + bMY) / R * x + Pxa + Pyb + c = 0;
let A = b * L;
let B = a * L;
let C = (a * Mx + b * My) / R;
let D = a * Px + b * Py + c;
// Asin(x) + Bcos(x) + Cx + D = 0;
// asinθ + bcosθ = √a²+b² sin (θ + α)
// f(x) = E * sin(x + Alpha) + Cx + D ... Fx(x, E, Alpha, C, D)
// f'(x) = E * cos(x + Alpha) + C ... F_Prime(x, E, Alpha, C)
let E = Math.sqrt(A * A + B * B);
let Alpha = Math.acos(A / E);
// Find all x when F'(x) = 0
// f'(x) = E * cos(x + Alpha) + C = 0
// cos(x + Alpha) = -C / E
// x + Alpha = acos( -C / E)
// x = acos(-C / E) - Alpha ... getZeroOf_F_Prime(Alpha, C, E)
let ZeroTime = getZeroTimeOf_F_Prime(Alpha, C, E);
// Set range of check
let endK;
if (minTime === undefined) { // means have parallel move and hit infinity times
endK = 20; // Limiting number of check
}
else {
let startAdjust = (minTime - ZeroTime) / (2 * Math.PI);
startAdjust = Math.floor(startAdjust);
ZeroTime += startAdjust * 2 * Math.PI;
endK = (maxTime - ZeroTime) / (2 * Math.PI);
endK = Math.ceil(endK) + 1;
if (endK > 20) {
endK = 20; // Limiting number of check
}
}
// Get distance values in the range
let distance = [];
let checkTime;
for (let loop = 0; loop < endK; loop++) {
checkTime = (ZeroTime + 2 * Math.PI * loop) * R;
distance[loop] = Fx(checkTime, E, Alpha, C, D);
console.log(checkTime / R + ' : ' + distance[loop]);
};
let epsilon = 0.00001;
let answerTime = [];
let answerNum = 0;
for (let loop1 = 0; loop1 < endK - 1; loop1++) {
if (distance[loop1] * distance[loop1 + 1] < -1) { // hit moment shoud be between here
// Newton method iterating
let time = (ZeroTime + 2 * Math.PI * (loop1 + 0.5)) * R;
time = Newton(time, E, Alpha, C, D);
let prevTime = time;
let loop2;
for (loop2 = 0; loop2 < 5; loop2++) {
time = this.Newton(time, E, Alpha, C, D);
console.log(' iterate: ' + time / R);
if (Math.abs(prevTime - time) < epsilon) {
break;
}
prevTime = time;
};
if (loop2 >= 5) { // Usually iteration should convergence less than 5 times
console.warn('Something wrong!');
}
console.log('Answer: ' + time / R);
answerTime[answerNum] = time / R;
answerNum++;
}
}
return answerTime;
}
// **********************************************************
// Return the moment when the distance increase or decrease becomes zero
// **********************************************************
getZeroTimeOf_F_Prime(Alpha, C, E) {
return Math.acos(-C / E) - Alpha;
}
// **********************************************************
// Return the distance
// **********************************************************
Fx(x, E, Alpha, C, D) {
return E * Math.sin(x + Alpha) + C * x + D;
}
// **********************************************************
//
// **********************************************************
F_Prime(x, E, Alpha, C) {
return E * Math.cos(x + Alpha) + C;
}
// **********************************************************
// Newton Method iterating function
// **********************************************************
Newton(x, E, Alpha, C, D) {
return x - Fx(x, E, Alpha, C, D) / F_Prime(x, E, Alpha, C);
}