Rotation Matrix given angle and point in X,Y,Z
Asked Answered
H

3

5

I am doing image manipulation and I want to rotate all of the pixels in xyz space based on an angle, the origin, and an x,y, and z coordinate.

I just need to setup the proper matrix (4x4) and then I will be good from there. The Angle is in degrees, not radians and the x,y,z are all going to be from -1 to 1 (floats)

EDIT:

Ok, here is the code that I whipped up to do the rotation about a given line defined by the origin and an X, Y, Z coorinate.

        float ang = angD * (float)(Math.PI / 180);  // from degrees to radians, if needed
        //U = n*n(t) + cos(a)*(I-n*n(t)) + sin(a)*N(x).

        var u = MatrixDouble.Identity(4);  // 4x4 Identity Matrix
        u = u.Multiply(Math.Cos(ang));

        var n = new MatrixDouble(1, 4, new List<double> { x, y, z, 0 });
        var nt = n.Transpose();

        // This next part is the N(x) matrix.  The data is inputted in Column
        // first order and fills in the 4x4 matrix with the given 16 Doubles
        var nx = new MatrixDouble(4, 4, new List<double> { 0, z, -y, 0, -z, 0, x, 0, y, -x, 0, 0, 0, 0, 0, 1 });

        nx = nx.Multiply(Math.Sin(ang));

        var ret = nt.Multiply(n);
        ret[3, 3] = 1;

        u = u.Subtract(ret);

        u = ret.Add(u.Add(nx));

It's a little complicated and I'm using a custom Matrix library, but nothing up there should be too hard to implement with any functioning Matrix lib.

Phew, lots of math!

Hostility answered 4/3, 2011 at 2:3 Comment(4)
I'm guessing you want the point defined by the "x, y and z coordinate" to remain invariant. How are you representing a point in xyz space as a 4-vector?Thoughtout
I'm not representing the points as a 4x4 vector. The transformation matrix is the 4x4 and the point is a 4x1. Multiplying them together gets me my p' which is the pixel only rotated. I'll update my post with the code that I made.Hostility
A "4x4 vector"? You are not being careful with terminology.Thoughtout
Wow, I feel dumb. 4x4 Matrix :) lol, at least I didn't say Matrice!!! heeheeHostility
B
11

The complete rotation matrices are derived and given at https://sites.google.com/site/glennmurray/glenn-murray-ph-d/rotation-matrices-and-formulas/rotation-about-an-arbitrary-axis-in-3-dimensions.

From the paper:

5.2 The simplified matrix for rotations about the origin

Note this assumes that (u, v, w) is a direction vector for the axis of rotation and that u^2 + v^2 + w^2 = 1.

Simplified 3D matrix for rotations about the origin.

If you have a point (x, y, z) that you want to rotate, then we can obtain a function of of seven variables that yields the rotated point:

f(x, y, z, u, v, w, theta) =

Formula for rotated point.

The paper also includes matrices and formulas for rotations about an arbitrary axis (not necessarily through the origin), Java code available under the Apache license, and a link to a web app that illustrates rotations.

Bristling answered 4/3, 2011 at 2:6 Comment(2)
Thou the math was a bit much to digest, this did end up working. It took me a bit, but I got it working and it is a general case. Thank you!Hostility
hello Glenn, i know this is so old answer but the link which is given by you is dead. If possible can you make an update?Waterless
O
2

Use the Matrix3D Structure (MSDN) - Represents a 4 x 4 matrix used for transformations in 3-D space

Take a look here for a tutorial: Building a 3D Engine

Essentially, matrices are built for X, Y, and Z rotations and then you can multiply the rotations in any order.

public static Matrix3D NewRotateAroundX(double radians)
{
    var matrix = new Matrix3D();
    matrix._matrix[1, 1] = Math.Cos(radians);
    matrix._matrix[1, 2] = Math.Sin(radians);
    matrix._matrix[2, 1] = -(Math.Sin(radians));
    matrix._matrix[2, 2] = Math.Cos(radians);
    return matrix;
}
public static Matrix3D NewRotateAroundY(double radians)
{
    var matrix = new Matrix3D();
    matrix._matrix[0, 0] = Math.Cos(radians);
    matrix._matrix[0, 2] = -(Math.Sin(radians));
    matrix._matrix[2, 0] = Math.Sin(radians);
    matrix._matrix[2, 2] = Math.Cos(radians);
    return matrix;
}
public static Matrix3D NewRotateAroundZ(double radians)
{
    var matrix = new Matrix3D();
    matrix._matrix[0, 0] = Math.Cos(radians);
    matrix._matrix[0, 1] = Math.Sin(radians);
    matrix._matrix[1, 0] = -(Math.Sin(radians));
    matrix._matrix[1, 1] = Math.Cos(radians);
    return matrix;
}
Orthodoxy answered 4/3, 2011 at 2:33 Comment(3)
While it is true that you can multiply the matrices in any order, you will not necessarily get the same answer if you do so. Matrix multiplication is not commutative. I give an example of products of rotation matrices about the axes giving different answers for different orders of multiplication in the paper linked at in my answer.Bristling
While it is true that MatrixX * MatrixY does not necessarily equal MatrixY * MatrixX, it is up to the OP to decide the order of multiplication.Orthodoxy
You're confusing 2 different Matrix3D's. Even though you reference the MSDN, that Matrix 3D doesn't have a "_matrix" member.Expositor
K
1

Function rotateAroundAxis() rotates point around any axis in 3D. It is my solution to the rotation in 3D using analytic geometry and programming to model the process. The code is in JavaScript.

function rotateAroundAxis(A, B, C, alpha, precision) {
  // A is rotated point, BC is axis, alpha is angle
  // A, B, C are points in format [Ax, Ay, Az], alpha is float, precision is int
  // A2 is output in format [A2x, A2y, A2z]
  if((A[0] - B[0])*(A[1] - C[1]) == (A[1] - B[1])*(A[0] - C[0]) && (A[1] - B[1])*(A[2] - C[2]) == (A[1] - C[1])*(A[2] - B[2]) && (A[0] - B[0])*(A[2] - C[2]) == (A[0] - C[0])*(A[2] - B[2])) {
    return A
  }// Return the original point if it is on the axis.
  var D = findClosestPoint(A, B, C, precision);
  var w = crossProduct(new Array(C[0] - B[0], C[1] - B[1], C[2] - B[2]), new Array(C[0] - A[0], C[1] - A[1], C[2] - A[2]));
  var W = pointPlusVector(A, w);
  var sizeAW = vectorSize(A, W);
  var sizeDA = vectorSize(D, A);
  var sizeAE = sizeDA*(Math.sin(0.5*alpha))/(Math.cos(0.5*alpha));
  var E = new Array(A[0] + (W[0] - A[0])*sizeAE/sizeAW, A[1] + (W[1] - A[1])*sizeAE/sizeAW, A[2] + (W[2] - A[2])*sizeAE/sizeAW);
  var sizeDE = vectorSize(D, E);
  var sizeEF = sizeAE*Math.sin(alpha/2);
  var F = new Array(D[0] + (E[0] - D[0])*(sizeDE - sizeEF)/sizeDE, D[1] + (E[1] - D[1])*(sizeDE - sizeEF)/sizeDE, D[2] + (E[2] - D[2])*(sizeDE - sizeEF)/sizeDE);
  var A2 = new Array(A[0] + 2*(F[0] - A[0]), A[1] + 2*(F[1] - A[1]), A[2] + 2*(F[2] - A[2]))
  return A2;
}

function angleSize(A, S, B) {
  ux = A[0] - S[0]; uy = A[1] - S[1]; uz = A[2] - S[2];
  vx = B[0] - S[0]; vy = B[1] - S[1]; vz = B[2] - S[2];
  if((Math.sqrt(ux*ux + uy*uy + uz*uz)*Math.sqrt(vx*vx + vy*vy + vz*vz)) == 0) {return 0}
  return Math.acos((ux*vx + uy*vy + uz*vz)/(Math.sqrt(ux*ux + uy*uy + uz*uz)*Math.sqrt(vx*vx + vy*vy + vz*vz)));
}

function findClosestPoint(N, B, C, precision) {
  // We will devide the segment BC into many tiny segments and we will choose the point F where the |NB F| distance is the shortest.
  if(B[0] == C[0] && B[1] == C[1] && B[2] == C[2]) {return B}
  var shortest = 0;
  for(var i = 0; i <= precision; i++) {
    var Fx = Math.round(precision*precision*(B[0] + (C[0] - B[0])*i/precision))/(precision*precision);
    var Fy = Math.round(precision*precision*(B[1] + (C[1] - B[1])*i/precision))/(precision*precision);
    var Fz = Math.round(precision*precision*(B[2] + (C[2] - B[2])*i/precision))/(precision*precision);
    var sizeF = vectorSize(new Array(N[0], N[1], N[2]), new Array(Fx, Fy, Fz));
    if(i == 0 || sizeF < shortest) { // first run or condition
      shortest = sizeF;
      F = new Array(Fx, Fy, Fz);
    }
  }
  // recursion, if it is an outer point return findClosestPoint(we mirror further point in the closer one)
  if(F[0] == Math.round(precision*precision*(B[0]))/(precision*precision) && F[1] == Math.round(precision*precision*(B[1]))/(precision*precision) && F[2] == Math.round(precision*precision*(B[2]))/(precision*precision)) { // F == B
    if(Math.round(precision*precision*180*angleSize(C, B, N)/Math.PI)/(precision*precision) <= 90){return F} else {return findClosestPoint(N, new Array(2*B[0] - C[0], 2*B[1] - C[1], 2*B[2] - C[2]), B, precision)}
  } else if (F[0] == Math.round(precision*precision*(C[0]))/(precision*precision) && F[1] == Math.round(precision*precision*(C[1]))/(precision*precision) && F[2] == Math.round(precision*precision*(C[2]))/(precision*precision)) { // F == C
    if(Math.round(precision*precision*180*angleSize(B, C, N)/Math.PI)/(precision*precision) <= 90) {return F} else {return findClosestPoint(N, C, new Array(2*C[0] - B[0], 2*C[1] - B[1], 2*C[2] - B[2]), precision)}
  } else {return F;}
}

function vectorSize(A, B) {
  var ux = A[0] - B[0];
  var uy = A[1] - B[1];
  var uz = A[2] - B[2];
  return Math.sqrt(ux*ux + uy*uy + uz*uz);
}

function crossProduct(u, v) {
  return (new Array(u[1]*v[2] - u[2]*v[1], u[2]*v[0] - u[0]*v[2], u[0]*v[1] - u[1]*v[0]));
}

function pointPlusVector (A, v) {
  return (new Array(A[0] + v[0], A[1] + v[1], A[2] + v[2]));
}
Killough answered 12/10, 2015 at 17:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.