Geometric rotations of a group of objects

geometryrotations

Referring to the first diagrams I am trying to copy the three objects, looking at them from an arbitrary angle(A1). The distance between where I am and the first object does not matter just the relative location of the object to one another.

In the second diagram, I select a point to copy these objects, facing another arbitrary angle(B1).
Angle (C1) shows the approximate position of -90 degrees.

I can get this to work if A1 = 0,90,180,270 and even 45,135 etc but the equations I come up with only work for 0 and 180 or 90 and 270. I have to modify them to work in those directions by changing a hardcoded offset angle and putting/removing a negative sign before the offset.

I am doing this is javascript (and its Minecraft) but 99% of this is math so that why I am asking here, usually I can figure out this but I have been working on it for weeks.

Here is some pseudo-code that works some of the time in certain right-angle directions. I have updated this to be more accurate, the 1x and 2x are the blocks x coordinate, etc. – everything is relative from the (1) block.
Minecraft's coordinate system is a little different from normal – 0 is south, +90 is west, 180 is north, 270 is east.
the only difference is that I am making negative az, ax.

// works for north/south looking - A1 is either 180/0 , B1 can be anything
var x = 1x - 2x;
var z = 1z - 2z;
var direction = Math.atan2(z1, x1);
var L1 = Math.sqrt(Math.pow(x1, 2) + Math.pow(z1, 2));
var az = Math.round(L1 * Math.sin((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));
var ax = Math.round(L1 * Math.cos((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));

// works for east/west looking - A1 is either 90/270 , B1 can be anything
var x = 1x - 2x;
var z = 1z - 2z;
var direction = Math.atan2(z1, x1);
var L1 = Math.sqrt(Math.pow(x1, 2) + Math.pow(z1, 2));
var az = -Math.round(L1 * Math.sin((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));
var ax = -Math.round(L1 * Math.cos((B1 + A1 + (direction * 180 / Math.PI)) * Math.PI / 180));

Thanks
First diagram

enter image description here

Second diagram

enter image description here

Best Answer

I shall assume this is intended as a 2D problem.

Let's say you are facing in direction $\theta_1$, when you pick $N$ objects near point $\vec{p} = (p_x , p_y)$. You calculate their location with respect to $\vec{p}$ – by substracting $\vec{p}$ from their locations –, and save them as e.g. $\vec{s}_1 = (s_{1 x}, s_{1 y})$ through $\vec{s}_N = (s_{N x}, s_{N y})$.

Then, you are facing in direction $\theta_2$, and wish to put back the $N$ objects near point $\vec{q} = (q_x , q_y)$, with their relative positions also rotated.

In 2D, the rotation counterclockwise (starting from $0$ on positive $x$ axis, then turning to positive $y$ axis) by angle $\varphi$ around point $(x_\text{center}, y_\text{center})$ is described by $$\left\lbrace ~ \begin{aligned} x_\text{new} &= x_\text{center} + (x_\text{old} - x_\text{center})\cos(\varphi) - (y_\text{old} - y_\text{center})\sin(\varphi) \\ y_\text{new} &= y_\text{center} + (x_\text{old} - x_\text{center})\sin(\varphi) + (y_\text{old} - y_\text{center})\cos(\varphi) \\ \end{aligned} \right.$$ This means that the new coordinates $\vec{z}_k = (z_{k x}, z_{k y})$ for object $k = 1 \dots N$ are $$\left\lbrace ~ \begin{aligned} z_{k x} &= q_x + s_{k x} \cos(\theta_2 - \theta_1) - s_{k y} \cos(\theta_2 - \theta_1) \\ z_{k y} &= q_y + s_{k y} \sin(\theta_2 - \theta_1) + s_{k x} \cos(\theta_2 - \theta_1) \\ \end{aligned} \right.$$

This works, because the coordinates $(s_{k x}, s_{k y})$ are already relative to the pick-up/drop-off point. You can choose any of the $N$ objects as the pick-up point, it's completely up to you. The reason the rotation angle is $(\theta_2 - \theta_1)$ is that those coordinates $(s_{k x}, s_{k y})$ have already been rotated by $\theta_1$, so we want to apply a rotation that consists of "(back from $\theta_1$ to basis, and then from basis to $\theta_2$)".

(If this was 3D, then $\theta_1$ and $\theta_2$ would be represented by versors (unit quaternions) or rotation matrices, say $\mathbf{R}_1$ and $\mathbf{R}_2$, and the rotation applied would be $\mathbf{R}_2\mathbf{R}_1^{-1} = \mathbf{R}_2\mathbf{R}^T$ if the matrices describe pure rotations. Otherwise the answer is the same for 3D too.)

As to OP's example code, in JS trigonometric functions use radians, not degrees, so the multiplications by 180/PI are absolutely incorrect.

In Javascript, you can use for example

var rotate(angledelta, offset, points) {
    var c = Math.cos(angledelta);
    var s = Math.sin(angledelta);
    var result = [];
    for (p of points) {
        result.push({ x:offset.x + c*p.x - s*p.y, y:offset.y + s*p.x - c*p.y });
    }
    return result;
}

where the first parameter is the difference in the angles in radians ($\theta_2 - \theta_1$), the second parameter is the drop target location, and the third parameter is an array of relative point locations to rotate. The function returns the list of rotated and translated locations. All locations are objects with x and y.

As an example,

rotate(90*Math.PI/180, {x:50, y:50}, [{x:0, y:35}]);

returns

[{15, 50}]

This means that if you pick up something relative to your pick up point $(0, 35)$, then rotate 90 degrees counterclockwise, and drop it with relative to $(30, 0)$, the objects coordinates will be $(15, 50)$.