+ 2

How to rotate a 3D point around arbitrary axis in JavaScript?

I want to implement a function with parameters like the following: /* @param axisX, axisY, axisZ would be components of a direction vector representing the arbitrary axis of rotation. The vector can be assumed to have a length of 1(a unit vector). @param radians would be an angle of rotation @param point would be something like an Array of length 3 with 3 numbers. This is the point to get rotated. */ function rotateArbitrary(axisX, axisY, axisZ, radians, point) { } I would prefer reusing a third party math library because this is a fairly complicated operation. If not, copy/pasting working JavaScript code would be a good alternative. It is complicated enough that I'm very happy ignoring how it works and mentally treating it like a black box. I found this example that uses three.js but three.js isn't a math library and includes a lot of graphics-related code that I don't want to use: https://stackoverflow.com/questions/11060734/how-to-rotate-a-3d-object-on-axis-three-js This shows how to rotate around x, y, and z axis but that's not flexible enough for my needs: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#rotation_matrix That is a sequence of 3 rotations around each of the x, y, z axis separately. That's not the same as my problem where an arbitrary axis is described with (x, y, z) vector components. Here is a 2D rotation but 2D rotation is very simple and not my problem: https://mathjs.org/docs/reference/functions/rotationMatrix.html It looks like c++ also has a library similar to what I want in JavaScript: https://stackoverflow.com/questions/62239604/how-to-rotate-a-point-around-an-arbitrary-axis This appears to explain the math and gives some c code but I'd prefer using a third party library that has already been tested rather than implement and test it myself: http://paulbourke.net/geometry/rotate/

26th Feb 2021, 3:58 PM
Josh Greig
Josh Greig - avatar
18 Respostas
+ 3
you should do it in 3 steps: 1) Rotate object so that rotation axis coincide with any of coordinate axis. 2) Perform rotation about co-ordinate axis with whom coinciding is done. 3) Apply inverse rotation to bring object back to the original rotation (cancel first rotation). obviously, you could do that for any axis (even not intersecting origin) if you wrap the whole operation in translations (which could be computed at once with the object transform matrix to have axis of rotation coinciding with global axis -- and its converse, after the "real" rotation) so, you just need a matrix multiplication function and at least one basic (3D) rotation matrix ;)
26th Feb 2021, 11:20 PM
visph
visph - avatar
+ 3
Jason Stone Awesome! Makes me want to do some prototyping now. Dang work and side projects keep me too occupied. Do you still have your codes where you implemented these rotations? cc: Runtime Terror This might be interesting to you as well. UPDATE: I just found the code. https://code.sololearn.com/W1gA0D3VnzW5/?ref=app
18th Jun 2021, 5:14 AM
David Carroll
David Carroll - avatar
+ 2
visph thanks. I'll try that. I'm surprised third party math libraries in JavaScript don't support this but will implement and test off your algorithm.
27th Feb 2021, 7:17 PM
Josh Greig
Josh Greig - avatar
+ 2
visph You're using rotations, I'm bending one axis and squishing the coordinate system in a weird, skewy manner. It's similar yes but what I'm doing breaks if x=0 for example. You'd need to add some extra if statements to make it work so it's just better to do what you're proposing. Never hurts to throw different solutions at the problem though :)
28th Feb 2021, 6:26 PM
Schindlabua
Schindlabua - avatar
+ 2
I'll have to check out that video. I'm not sure if you need further references, but I highly recommend checking out the content by Ben Eater and Grant Sanderson (a.k.a. 3Blue1Brown on YouTube). https://eater.net/quaternions It's been one of my favorite references for developing an intuition about quaternions. Jason Stone is someone here who has implemented some impressive codes demonstrating quaternion rotation. Perhaps he will be able to chime in here.
17th Jun 2021, 8:38 AM
David Carroll
David Carroll - avatar
+ 1
visph's approach is better but you could also try a change of basis. Choose (x,y,z) as your new x axis, keep (0,1,0) and (0,0,1) for y and z axes. This gives us a change of basis matrix of x 0 0 y 1 0 z 0 1 Then multiply this matrix with a regular old rotation around the x axis: 1 0 0 0 cos φ sin φ 0 -sin φ cos φ Then multiply with the inverse change of basis matrix 1/x 0 0 -y/x 1 0 -z/x 0 1 and apply the resulting matrix to your point.
28th Feb 2021, 6:12 PM
Schindlabua
Schindlabua - avatar
+ 1
Schindlabua I cannot see the difference with my approach: didn't you just develop mathematicaly the concept I was explained? as I implicitly thinking to reduce the 3 matrices to only one before applying to target points ;)
28th Feb 2021, 6:19 PM
visph
visph - avatar
+ 1
A video here shows how to make a 4 by 4 matrix for this arbitrary rotation: https://www.programmingtil.com/contents/3d-math-quaternion-to-matrix-calculation The video starts with the formula. The rest of the video isn't really necessary if you just want something that works. Here is my JavaScript implementation for creating the rotation matrix and making a math.Matrix from math.js. _getRotationMatrix(rotation) { var nx = rotation[0]; var ny = rotation[1]; var nz = rotation[2]; var t = rotation[3]; var cosT = Math.cos(t); var sinT = Math.sin(t); var oMinusCosT = 1 - Math.cos(t); var values = [ [ nx*nx*oMinusCosT + cosT, nx*ny*oMinusCosT - nz*sinT, nx*nz*oMinusCosT + ny*sinT ], [ nx*ny*oMinusCosT+nz*sinT, ny*ny*oMinusCosT+cosT, ny*nz*oMinusCosT-nx*sinT, ], [ nx*nz*oMinusCosT-ny*sinT, ny*nz*oMinusCosT+nx*sinT, nz*nz*oMinusCosT+cosT ] ]; values.push([0,0,0]); for (var i = 0; i < 4; i++) values[i].push(0); values[3][3] = 1; return math.matrix(values); } You could then transform individual points with this: transform(point) { if (!(point instanceof Array) || point.length !== 3) throw new Error('point must be an Array and with length 3.'); for (var i = 0; i < 3; i++) if (typeof point[i] !== 'number' || isNaN(point[i])) throw new Error('point[' + i + '] must be a number but found: ' + point[i]); point = point.slice(0); point.push(1); var p = math.matrix(point); var result = math.multiply(this.transformationMatrix, p); return result._data.slice(0, 3); } math.js is available at: https://unpkg.com/mathjs@9.2.0/lib/browser/math.js
14th Jun 2021, 2:37 PM
Josh Greig
Josh Greig - avatar
+ 1
Oh, quaternions. They’re awesome, but being literal 4 dimensional complex numbers the formulas are a bit complicated and scary. For example, the multiplication operation involves 16 floating point multiplications. Anyway, I’ll dig around in some of my old codes and try to pry out a few quaternion functions.
18th Jun 2021, 4:15 AM
Jason Stone
Jason Stone - avatar
+ 1
Ok, here’s some code: class Quaternion { constructor(r, i, j, k) { this.r = r; this.i = i; this.j = j; this.k = k; } static mult(q1, q2) { var r = q1.r * q2.r - q1.i * q2.i - q1.j * q2.j - q1.k * q2.k; var i = q1.r * q2.i + q1.i * q2.r + q1.j * q2.k - q1.k * q2.j; var j = q1.r * q2.j - q1.i * q2.k + q1.j * q2.r + q1.k * q2.i; var k = q1.r * q2.k + q1.i * q2.j - q1.j * q2.i + q1.k * q2.r; return new Quaternion(r, i, j, k); } static inv(q) { return new Quaternion(q.r, -q.i, -q.j, -q.k); } } class Point { constructor(x, y, z) { this.i = x; this.j = y; this.k = z; } rotate(rotation) { var q = Quaternion.mult(Quaternion.mult(rotation, this), Quaternion.inv(rotation)); this.i = q.i; this.j = q.j; this.k = q.k; } } const Rotation = function(angle, x, y, z) { const scaleFactor = Math.sqrt(x ** 2 + y ** 2 + z ** 2); const c = Math.sin(angle / 2) / (scaleFactor || 1); return new Quaternion(Math.cos(angle / 2), c * x, c * y, c * z); }
18th Jun 2021, 4:21 AM
Jason Stone
Jason Stone - avatar
+ 1
The point stores it’s coordinates as i, j, and k instead of x, y, and z, respectively. The x, y, and z of the rotation function are the axis of rotation and the angle should be in radians. The rotation function returns a quaternion which can be passed to the point’s rotation method to rotate it.
18th Jun 2021, 4:24 AM
Jason Stone
Jason Stone - avatar
+ 1
David Carroll actually, all my 3D codes use quaternions. So, this one, the lorenz attractor one, and my WebGL ones (including Traquair Maze) all use quaternions. I’m pretty sure those are the only places I’ve used them.
18th Jun 2021, 7:00 AM
Jason Stone
Jason Stone - avatar
+ 1
Also, here’s my lorenz attractor in circuit form via the everycircuit app: https://www.sololearn.com/post/648185/?ref=app (I have to give a link via a post because SL doesn’t feel like accepting non-SL links today). To activate the circuit, just tap the switch near the bottom-right corner (look up “circuit switch symbol” if you can’t find it). Also, the browser version only works on desktop, so get the app if you’re on mobile. If you have the app and the link still takes you to the website, then copy/paste the sixteen digit number at the end of the link into the search bar in the everycircuit app.
18th Jun 2021, 7:34 AM
Jason Stone
Jason Stone - avatar
0
Miriella wrote, "It's still the same as rotating a 2d vector about an arbitrary axis, just add the z-component, translate to the origin and rotate. PS: I can't write the code with my phone cos it's long but if you have a template, I'll try and complete the function for you" Response: How would you even have an arbitrary axis to rotate around in 2D? In 2D, you can rotate around an arbitrary point but that's not an axis and that is far simpler to do. In 2D, the only rotation directions are clockwise and counterclockwise and the position of the point has no influence on the rotation direction. In 3D, there are an infinite number of possible axis to rotate around. I hope you understand my question because I'm sure it isn't that simple to rotate around an axis with arbitrary direction(not just pointing in one of the x, y, or z directions). The template was given in the question. Here is another copy: /* @param axisX, axisY, axisZ would be components of a direction vector representing the arbitrary axis of rotation. The vector can be assumed to have a length of 1(a unit vector). @param radians would be an angle of rotation @param point would be something like an Array of length 3 with 3 numbers. */ function rotateArbitrary(axisX, axisY, axisZ, radians, point) { } Note that there is no translation required in this. The axis of rotation always intersects (0, 0, 0). If you read my question again, you'll see there is no position vector for the axis of rotation and only a direction vector. I mention this because you seem to be talking about rotating around a point in 2D which is a misunderstanding of my question.
26th Feb 2021, 5:45 PM
Josh Greig
Josh Greig - avatar
0
I implemented visph's steps to the best of my understanding. It looks like I did something wrong but that's likely in how I translated the steps rather than the steps visph gave. The implementation is at: https://joshi1983.github.io/pages/pointCloudLoaderPrototype/arbitraryAxisRotation.js It depends on math.js( https://unpkg.com/mathjs@9.2.0/lib/browser/math.js ) for defining matrix multiplication. I tested it by loading some X3D files into this that included rotation transformations: https://joshi1983.github.io/pages/pointCloudLoaderPrototype/ I loaded this and it looks like points from the spokes are rotated nicely but points in the bike frame show in unexpected areas compared to the X3D viewer here: https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/DirtBikeIndex.html It might not even be a problem with the rotation. I'll keep troubleshooting this after adding some X3D support to my mesh viewer at https://joshi1983.github.io/pages/meshViewer/. If anyone is curious to troubleshoot my implementation, I added a copy of the Point Cloud Loader Prototype in sololearn at: https://code.sololearn.com/Wa13A20A31A3/#
1st Mar 2021, 1:11 AM
Josh Greig
Josh Greig - avatar