如何在Javascript中计算2D旋转

54

我对三角学并不是很熟悉,但我只需要在二维平面上旋转两个点:

                    *nx, ny
               .     -
          .           -
     .  angle          -
*cx,cy.................*x,y

cx,cy是旋转中心
x,y是当前坐标
nx,ny是新坐标

如何计算在给定角度下的新点?


1
所以你有一个三角形,现在想要在给定新角度的情况下计算nx和ny的位置? - Samuel Reid
5个回答

146
function rotate(cx, cy, x, y, angle) {
    var radians = (Math.PI / 180) * angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
        ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    return [nx, ny];
}

前两个参数是中心点的X和Y坐标(围绕第二个点旋转的原点)。接下来的两个参数是我们将要旋转的点的坐标。最后一个参数是角度(以度为单位)。

例如,我们将以点(2,1)为例,以顺时针90度旋转它围绕点(1,1)旋转。

rotate(1, 1, 2, 1, 90);
// > [1, 0]

关于这个函数有三点需要注意:

  1. 对于顺时针旋转,最后一个参数angle应该为正数。对于逆时针旋转(如您提供的图表),它应该是负数。

  2. 请注意,即使您提供了应生成坐标为整数的点的参数--例如,将点(5,0)绕原点(0,0)旋转90度,应得到(0,-5)--JavaScript的舍入行为意味着每个坐标仍然可能是一个接近预期整数但仍为浮点数的值。例如:

  3. rotate(0, 0, 5, 0, 90);
    // > [3.061616997868383e-16, -5]
    

    因此,应该预期生成的数组的两个元素都是浮点数。您可以根据需要使用Math.round()Math.ceil()Math.floor()将它们转换为整数。

  4. 最后,请注意,此函数假定笛卡尔坐标系,这意味着Y轴上的值随着在坐标平面中向“上”移动而变大。在HTML / CSS中,Y轴被反转 - Y轴上的值随着向页面下方移动而变大。


1
那应该是“+”。谢谢! - theftprevention
1
在SO上,大约有50%的答案在x公式中有“-”,另外一半在y公式中有。在我的情况下,它似乎只在y公式中有效。这是怎么回事? - Michael
谢谢!我得重新审视我的工作:我不知怎么解决了问题,似乎它能正常工作,但现在我却不记得我当时做了什么... - Michael
@JunaidShirwani,我无法复制您所描述的行为。每当我将第5个参数(angle)设置为0时,我总是得到一个包含第3个和第4个参数([x,y])未修改的数组。您看到了不同的东西吗? - theftprevention
2
关于答案中提到的第三点,如果有人想要使用此函数来获取相对于DOM坐标系工作方式的坐标,则只需进行微小更改即可得到正确的输出 - 只需将_nx_和_ny_公式中的算术符号反转,如下所示: nx = (cos * (x - cx)) - (sin * (y - cy)) + cx, ny = (cos * (y - cy)) + (sin * (x - cx)) + cy; - prateekj_ahead
显示剩余3条评论

7
  1. 首先,将旋转中心平移到原点
  2. 计算新坐标(nx, ny)
  3. 再将其平移回原始的旋转中心

步骤1

您的新点为

  1. 中心:(0,0)
  2. 点:(x-cx, y-cy)

步骤2

  1. nx = (x-cx)*cos(theta) - (y-cy)*sin(theta)
  2. ny = (y-cy)*cos(theta) + (x-cx)*sin(theta)

步骤3

将其平移回原始的旋转中心:

  1. nx = (x-cx)*cos(theta) - (y-cy)*sin(theta) + cx
  2. ny = (y-cy)*cos(theta) + (x-cx)*sin(theta) + cy

想要了解更深入的解释和一些精美的图表,请参考这个链接.


6

上面接受的答案对我来说并不正确,旋转是相反的,这里是可行的函数

/*
 CX @ Origin X  
 CY @ Origin Y
 X  @ Point X to be rotated
 Y  @ Point Y to be rotated  
 anticlock_wise @ to rotate point in clockwise direction or anticlockwise , default clockwise 
 return @ {x,y}  
*/
function rotate(cx, cy, x, y, angle,anticlock_wise = false) {
    if(angle == 0){
        return {x:parseFloat(x), y:parseFloat(y)};
    }if(anticlock_wise){
        var radians = (Math.PI / 180) * angle;
    }else{
        var radians = (Math.PI / -180) * angle;
    }
    var cos = Math.cos(radians);
    var sin = Math.sin(radians);
    var nx = (cos * (x - cx)) + (sin * (y - cy)) + cx;
    var ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    return {x:nx, y:ny};
 }

1
如果角度为零,则将坐标转换为整数会截断小数。你至少应该转换为浮点数。此外,你的参数是大写的,但函数中使用的是小写的。 - I wrestled a bear once.

4
根据维基百科上的极坐标系条目
x = r * cos(deg)
y = r * sin(deg)
  • r半径)等于旋转中心和旋转点之间的距离
  • deg度数)是以度为单位测量的角度

1

我认为对于这样的操作,使用矩阵更好。

以下是使用gl-matrix的示例(但也可以使用类似于THREEJS的库)。

import * as glm from 'gl-matrix';
const rotateVector = (() => {
  
  const q = glm.quat.create();  
  // const m = glm.mat4.create(); // 2nd way

  return (v: glm.vec3, point: glm.vec3, axis: glm.vec3, angle: number) => {

      glm.quat.setAxisAngle(q, axis, angle);
      // glm.mat4.fromRotation(m, angle, axis); // 2nd way
      glm.vec3.sub(v, v, point);
      glm.vec3.transformQuat(v, v, q);
      // glm.vec3.transformMat4(v, v, m); // 2nd way
      glm.vec3.add(v, v, point);
      return v;
  }
})();

在二维情况下,您需要围绕z轴旋转:

rotateVector([x, y, 0], [cX, cY, 0], [0, 0, 1], angleInRadians);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接