计算从角旋转的正方形后的偏移量

cyb*_*bat 1 javascript trigonometry rotation

当我旋转一个正方形时,我想从旋转点计算 4 个偏移量。

旋转轴最初位于正方形的左上角。当我执行旋转时,我想知道形状将在所有 4 个方向(minX、minY、maxX、maxy)上传播多远。

我目前有一般数学:

const rotation = .35  // radians = 20 degrees
const size = 50 // size of original square

const o1 = Math.round(size * Math.sin(rotation))
const o2 = Math.round(size * Math.cos(rotation))
Run Code Online (Sandbox Code Playgroud)

使用这些数字,我看到了如何使用它们来创建偏移量数组

const offsets = [o1, 0, o2, o1 + o2]
Run Code Online (Sandbox Code Playgroud)

当我将正方形从 20、110、200 和 290 度旋转时,它将围绕图像上黑点标记的轴旋转。

旋转方块

对于 4 次旋转中的每一次,我都有 offests 数组以及我想要的实际数字。正如您所看到的,这些数字在那里,但是......我最初认为我只需要一个数组移位,但它不止于此。

// 20 degrees
console.log(offsets) // [17, 0, 47, 64] 
// The dimensions I actually need
// minX: -17,
// minY: 0
// maxX: 47
// maxY: -64

// 110 degrees
console.log(offsets) // [47, 0, -17, 30] 
// The dimensions I actually need
// minX: -64,
// minY: -17,
// maxX: 0,
// maxY: 47

// 200 degrees
console.log(offsets) // [-17, 0, -47, -64] 
// The dimensions I actually need
// minX: -47,
// minY: -64,
// maxX: 17,
// maxY: 0

// 290 degrees
console.log(offsets) // [-47, 0, 17, -30] 
// The dimensions I actually need
// minX: 0,
// minY: -47,
// maxX: 64,
// maxY: 17
Run Code Online (Sandbox Code Playgroud)

如果需要,我当然可以移动数组(比如每 90 度),但我怎样才能得到正确的数字?我正在寻找任何角度的神奇公式。

Bli*_*n67 5

变换点

最简单的方法是创建一个简单的旋转矩阵。这只是作为向量的 x 轴和 y 轴的方向,每个向量的长度都是一个像素的大小(或任何可能的单位)和原点的位置。

旋转一个点

首先定义点

var x = ?;  // the point to rotate
var y = ?;
Run Code Online (Sandbox Code Playgroud)

然后原点和旋转

const ox = ?; // location of origin
const oy = ?;
const rotation = ?; // in radians
Run Code Online (Sandbox Code Playgroud)

从旋转我们计算到向量,即 x 轴的方向

var xAxisX = Math.cos(rotation);
var xAxisY = Math.sin(rotation);
Run Code Online (Sandbox Code Playgroud)

(可选)你也可以有一个规模

const scale = ?;
Run Code Online (Sandbox Code Playgroud)

这会改变 x 和 y 轴的长度,所以 x 轴的计算是

var xAxisX = Math.cos(rotation) * scale;
var xAxisY = Math.sin(rotation) * scale;
Run Code Online (Sandbox Code Playgroud)

不,我们可以将旋转应用于该点。首先相对于原点移动点。

x -= ox;
y -= oy;
Run Code Online (Sandbox Code Playgroud)

然后沿x轴移动点x距离

var rx = x * xAxisX;
var ry = x * xAxisY;
Run Code Online (Sandbox Code Playgroud)

然后沿 y 轴移动 y 距离。y 轴与 x 顺时针成 90 度。要将任何向量旋转 90 度,您可以交换 x 和 y 并否定新的 x。因此沿y轴移动如下

rx -= y * xAxisY;  // use x axis y for y axis x and negate
ry += y * xAxisX;  // use x axis x for y axis y 
Run Code Online (Sandbox Code Playgroud)

现在该点已旋转但仍相对于原点,我们需要将其移回世界空间。为此,只需添加原点

rx += ox;
ry += oy;
Run Code Online (Sandbox Code Playgroud)

而 rx,ry 是围绕原点旋转的点,如果你这样做了,就会缩放。

在 2D 上下文中匹配旋转

你可以让 2D 上下文为你做同样的事情

ctx.setTransform(xAxisX, xAxisY, -xAxisY, xAxisX, ox, oy);
ctx.fillRect(x,y,1,1); // draw the rotated pixel
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default transform
Run Code Online (Sandbox Code Playgroud)

或者您可以通过函数调用添加旋转

ctx.setTransform(1, 0, 0, 1, ox, oy);
ctx.rotate(rotation);
// and if scale then 
// ctx.scale(scale,scale)
ctx.fillRect(x,y,1,1); // draw the rotated pixel
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default transform
Run Code Online (Sandbox Code Playgroud)

上面的各个步骤都可以压缩,答案的下一部分使用上述方法旋转一个矩形。

旋转矩形

以下函数将返回 4 个旋转的角。

// angle is the amount of rotation in radians
// ox,oy is the origin (center of rotation)
// x,y is the top left of the rectangle
// w,h is the width and height of the rectangle
// returns an array of points as arrays [[x,y],[x1,y1],...]
// Order of returned points topLeft, topRight, bottomRight, bottomLeft
function rotateRect(angle,ox,oy,x,y,w,h){
    const xAx = Math.cos(angle);  // x axis x
    const xAy = Math.sin(angle);  // x axis y
    x -= ox;  // move rectangle onto origin
    y -= oy; 
    return [[ // return array holding the resulting points
            x * xAx - y * xAy + ox,   // Get the top left rotated position
            x * xAy + y * xAx + oy,   // and move it back to the origin
        ], [
            (x + w) * xAx - y * xAy + ox,   // Get the top right rotated position
            (x + w) * xAy + y * xAx + oy,   
        ], [
            (x + w) * xAx - (y + h) * xAy + ox,   // Get the bottom right rotated position
            (x + w) * xAy + (y + h) * xAx + oy,   
        ], [
            x * xAx - (y + h) * xAy + ox,   // Get the bottom left rotated position
            x * xAy + (y + h) * xAx + oy,   
        ]
    ]; 
}
Run Code Online (Sandbox Code Playgroud)

查找偏移量

使用该功能

var angle = 1;  // amount to rotate in radians
var ox = 0;   // origin top left of rectangle
var oy = 0; 
const rotatedRect = rotateRect(angle,ox,oy,0,0,50,50);
const r = rotatedRect; // alias to make following code more readable
var leftOfOrigin  = Math.min(r[0][0],r[1][0],r[2][0],r[3][0]) - ox;
var rightOfOrigin = Math.max(r[0][0],r[1][0],r[2][0],r[3][0]) - ox;
var aboveOrigin   = Math.min(r[0][1],r[1][1],r[2][1],r[3][1]) - oy;
var belowOrigin   = Math.max(r[0][1],r[1][1],r[2][1],r[3][1]) - oy;
Run Code Online (Sandbox Code Playgroud)

我将距离计算保留在函数之外,因为这更有用,因为您可能需要有关旋转点的更多信息。

演示

举个例子

var x = ?;  // the point to rotate
var y = ?;
Run Code Online (Sandbox Code Playgroud)
const ox = ?; // location of origin
const oy = ?;
const rotation = ?; // in radians
Run Code Online (Sandbox Code Playgroud)
var xAxisX = Math.cos(rotation);
var xAxisY = Math.sin(rotation);
Run Code Online (Sandbox Code Playgroud)