Javascript 我怎样才能解这个三角问题

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,

我正在创建一个游戏,并希望解决这个问题,使我的游戏功能

AsinRX+BcosRX+CX+D=0 找到最小X值,该值等于或大于0,小于或等于1。 A、 B、C和R是变量

我可以使用反三角函数,如acos、asin等。但我想让计算尽可能准确

我试着用三角函数公式使它变得简单,但我做不到

更详细地说,, 有一个带移动和碰撞的旋转框


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

这仅在-1
X = X - f(X)/f_prime(X)
直到absfX 但是,如果导数没有零,那么可以选择X=0,然后再次使用牛顿方法。我想简单实现牛顿方法的Python代码片段可能如下所示

epsilon = 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);
}