如何从矩形点计算旋转角度?

27
我有四个点1234,它们组成一个矩形。

这些点按以下方式在数组中排列:x1y1x2y2x3y3x4y4

问题是这个矩形可以以任意角度旋转。

如何计算原始点(灰色轮廓)和角度?

enter image description here

我正在尝试在javascript+css3-transform中复制这种效果,因此需要首先了解直角尺寸,然后使用css进行旋转。
我只知道通过比较点(例如 y1==y2)来确定矩形是否是直角的。
if(x1==x4 && x2==x3 && y1==y2 && y4==y3){

    rectangle.style.top = y1;
    rectangle.style.left = x1;
    rectangle.style.width = x2-x1;
    rectangle.style.height = y4-y1;
    rectangle.style.transform = "rotate(?deg)";

}
3个回答

24

您可以使用同侧的任何坐标对来计算旋转角度。请注意,数学角通常假定0为正X轴,逆时针旋转增加(因此沿着正Y轴为90°,负X轴为180°等)。

此外,JavaScript三角函数返回弧度值,必须在用于CSS变换之前将其转换为度数。

如果形状旋转不超过90度,则比较简单,可以使用直角三角形的正切比率:

tan(angle) = length of opposite side / length of adjacent side

对于提问者来说,最好使用1和4两个角落,这样旋转保持在第一象限并且按顺时针方向进行(根据CSS3草案规范)。用JavaScript表达如下:

var rotationRadians = Math.atan((x1 - x4) / (y1 - y4));

转换为度数:

var RAD2DEG = 180 / Math.PI;
var rotationDegrees = rotationRadians * RAD2DEG;

如果旋转角度大于90°,则需要调整角度。例如,当角度大于90°但小于180°时,您将从上面得到一个负值,并需要添加180°:

  rotationDegrees += 180;

此外,如果您使用页面尺寸,y坐标向下增加,这与正常数学意义相反,因此您需要颠倒上面的 y1 - y4 的意义。

编辑

根据OP中点的方向,以下是一个通用函数,可返回矩形的中心和顺时针旋转角度。这就是您所需的全部内容,但如果愿意,您可以将角落旋转为“水平”。 您可以应用三角函数来计算新角或者只做一些平均值(类似于Ian的答案)。

/** General case solution for a rectangle
 *
 *  Given coordinages of [x1, y1, x2, y2, x3, y3, x4, y4]
 *  where the corners are:
 *            top left    : x1, y1
 *            top right   : x2, y2
 *            bottom right: x3, y3
 *            bottom left : x4, y4
 *
 *  The centre is the average top left and bottom right coords:
 *  center: (x1 + x3) / 2 and (y1 + y3) / 2
 *
 *  Clockwise rotation: Math.atan((x1 - x4)/(y1 - y4)) with
 *  adjustment for the quadrant the angle is in.
 *
 *  Note that if using page coordinates, y is +ve down the page which
 *  is the reverse of the mathematic sense so y page coordinages
 *  should be multiplied by -1 before being given to the function.
 *  (e.g. a page y of 400 should be -400).
 *
 * @see https://dev59.com/YWcs5IYBdhLWcg3wRBtQ#13003782
 */
function getRotation(coords) {
    // Get center as average of top left and bottom right
    var center = [(coords[0] + coords[4]) / 2,
                  (coords[1] + coords[5]) / 2];

    // Get differences top left minus bottom left
    var diffs = [coords[0] - coords[6], coords[1] - coords[7]];

    // Get rotation in degrees
    var rotation = Math.atan(diffs[0]/diffs[1]) * 180 / Math.PI;

    // Adjust for 2nd & 3rd quadrants, i.e. diff y is -ve.
    if (diffs[1] < 0) {
        rotation += 180;
      
    // Adjust for 4th quadrant
    // i.e. diff x is -ve, diff y is +ve
    } else if (diffs[0] < 0) {
        rotation += 360;
    }
    // return array of [[centerX, centerY], rotation];
    return [center, rotation];
}

非常感谢,我无法得到小于90度的角度,你能给我解释一下吗?看起来工作得很好,但有些小问题,http://jsfiddle.net/Victornpb/nBKAP/ 我也搞混了矩阵顺序,但我已经做出了改变。实际上,我正在尝试将Collada转换为js+css,但似乎是一条漫长的路,我不知道如何处理Z坐标。 - Vitim.us
小提琴中的点顺序和方向与您的帖子不同。看起来您正在使用笛卡尔坐标系,因此在上面的代码中,我有coords[0] - coords[6],您需要x3 - x4,而我有coords[1] - coords[7],您需要y3 - y4 - RobG
是的,Google SketchUp使用笛卡尔坐标系。所以我没有反转轴,而是将顶部属性与底部属性交换以匹配物体。而矩形点实际上是按另一种顺序排列的,实际上是从右下角开始逆时针[3,2][4,1],而不是从左上角开始顺时针[1,2][4,3]。 - Vitim.us

11

矩形的中心正好位于两个对角线之间:

cx = (x1 + x3) / 2
cy = (y1 + y3) / 2
矩形的尺寸是两点之间的距离:
w = sqrt(pow(x2-x1, 2) + pow(y2-y1, 2))
h = sqrt(pow(x3-x2, 2) + pow(y3-y2, 2))

可以通过矩形的中心和大小计算出其各个角落的位置,例如左上角:

x = cx - w / 2
y = cy - h / 2
角度是正方形边的反正切值:
a = arctan2(y4 - y1, x4 - x1)

(我不确定它返回的是哪个角度,或者说你期望得到什么角度,因此你可以进行一些测试。)


所需角度为顺时针旋转。如果 OP 使用页面坐标,则 Y 坐标的方向错误(+ve 向下翻页,而 Math 三角函数期望在该方向上为 -ve),因此您的表达式应为 Math.arctan2(-(y1 - y4), x1 - x4)。但这仅适用于第一象限,并且如果角度大于 90° 则需要进行调整(例如,在第二象限中它将给出 -45° 而不是 135°,在第三象限中它将给出 -135° 而不是 225°)。 - RobG
从图片中的蓝色坐标系指示器来看,OP使用的是一个向上为正的Y坐标轴,而不是向下为正。我不知道OP想要度数还是弧度,或者他是否只限于正角度。 - Guffa

0

这是获取垂直粉色线和从粉色线交叉点开始的黑色线之间角度的方法:

var deg = 90 - Math.arctan((x2-x1) / (y2-y1));

可以利用勾股定理计算尺寸:

var width = Math.sqrt((x2-x1)^2 / (y2-y1)^2));
var height = Math.sqrt((x1-x4)^2) / (y4-y1)^2));

位置坐标(左和上)分别是x1和x3的平均值以及y1和y3的平均值。

var left = Math.floor((x1 + x3) / 2);
var top = Math.floor((y1 + y3) / 2);

你想使用负边距技巧。

var marginLeft = -Math.ceil(width / 2);
var marginTop = -Math.ceil(height / 2);

4
Javascript的三角函数(例如Math.atan)返回弧度制的值。180°中有Math.PI个弧度。无论如何,你正在使生活变得比必要的更加困难。可以使用任何一侧的旋转,因此可以从同一侧的任何坐标对计算顺时针旋转角度(以弧度为单位),例如:Math.asin((x4-y1)/(y1-y4))。请注意,当旋转超过90°时,需要进一步处理。 - RobG

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