Rotating around an arbitrary axis without changing its previous orientation - javascript

So i am simulating a foreward kinematic chain in Three.js. The chain contents of cylindrical joints and static links inbetween. The links are static and should only be rotated and translated according to the kinematics. I get displacement matrices M with some rotation and a positional vektor for the joints. The displacement matrix determines also the rotation and position of the following joints, but you have to add the cylindrical rotation and translation for the joint to be in the correct place.
function updateLink(link, jointAxis, trans, rot_3, rotOffset, transOffset){
pre_link = new Object3D()
pre_link = link.clone() //use the old link from zero configuration
pre_link.matrix.setFromMatrix3(rot_3) // change the position and rotation due to
// movement of the chain
pre_link.matrix.setPosition(trans)
pre_link.matrixAutoUpdate = false;
The translation is just the translational offset along the joint axis(which is nomalized) :
trans = anchor + transOffSet * jointAxis
https://i.stack.imgur.com/Ni5Xb.png
Now the link is at the bottom of the joint, but it needs to get rotated around the joint axis to get in place, while not effecting the rotation and translation it got from the displacement matrix.
I tried out a bunch of things , iE that i add the joint to the cylinder and then rotate the parent:
cylinder.add(link)
cylinder.rotateOnAxis(jointAxis, rotOffset)
With this code, the link gets rotated around an axis that goes through the origin, but i want to rotate around general lines not going thorugh the origin. (The cylinder up Axis, which is some kind of normalized vector at a certain point in the coordinate system.)
Any help or ideas would be much aprreciated.

Related

Replicate object rotation inside rotated parent in Three.js

I built my 3D globe and placed it into a parent bounding box / pivot like this:
var globe = new THREE.Group();
if (earthmesh) {globe.add(earthmesh);};
if (linesmesh) {globe.add(linesmesh);};
if (cloudmesh) {globe.add(cloudmesh);};
if (atmosmesh) {globe.add(atmosmesh);};
pivot = new THREE.Group();
if (globe) {pivot.add(globe);};
if (pivot) {scene.add(pivot);};
so that when applying, say, a 23 degree rotation around the Z (far to near) axis on the parent:
pivot.rotation.set(THREE.Math.degToRad(0), THREE.Math.degToRad(0), THREE.Math.degToRad(23));
the globe would rotate around the Y (down to up) axis relative to its parent, in the animation function (in case you wonder, v is just an object holding the values for the .loop i.e. 360 degrees, .rate i.e. the rotation speed, as well as the .roll, .spin and .tilt variables corresponding to the X, Y and Z rotations of the globe, in degrees):
function animate()
{
delta = clock.getDelta();
resize();
v.spin = (v.loop + v.spin + v.rate * delta) % v.loop;
globe.rotation.set(THREE.Math.degToRad(v.roll), THREE.Math.degToRad(v.spin), THREE.Math.degToRad(v.tilt));
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
like the green arrow indicates below, instead of how the red arrow indicates if the globe had no rotated parent:
This is very simple and it works well, if I do something like:
pivot.rotation.set(THREE.Math.degToRad(v.roll), THREE.Math.degToRad(0), THREE.Math.degToRad(v.tilt));
globe.rotation.set(THREE.Math.degToRad(0), THREE.Math.degToRad(v.spin), THREE.Math.degToRad(0));
in the animation function above and my on-demand / manual rotation system, without changing the camera position / direction as that is handled as desired by the ArcballControls library of Three.js, also visible above.
However, I would like to achieve this object rotation inside its parent by using other tools available in Three.js, like matrices, vectors, quaternions or other built-in functions, without having to set a parent for the globe and distributing the rotations between the parent and the child. The idea is to do the globe rotations normally like in the original animation function above, and apply some of those other tools (or even formulas) to alter the rotation and produce the desired effect. How can I do that?
In the interest of clarity, the benefit of such an approach over creating a parent for the globe would be that:
I would be able to easily toggle between the "normal" (red arrow) and the "altered" (green arrow) rotation systems in an if (...) {...} condition without having to destroy, recreate or reset the parent - child construct
I would be able to freely use the rotations around all the 3 axes for both the globe and the altering method, instead of applying some rotations on the parent and some on the child
Also, just in case I'll eventually settle on the parent - child approach above if the other methods are too complicated, how would I "reset" the rotation system to the "original" (i.e. red arrow) way in that case, without changing the object's visual orientation? I mean, resetting the pivot rotation to (0, 0, 0) is clear, but what would be the rotation applied to the globe child so that it stays the same even if its parent is now not rotated?
Note: For simplicity, a (0, 0, 0) position is assumed for both the parent and the child groups / objects and the parent rotation is illustrated on a single axis, but ideally, the alternative should allow different hypothetical positioning for the pivot and the object, while the globe altering rotation should be possible on all 3 axes.
P.S. Feel free to suggest improvements to how the question is formulated in order to make it clearer or more compact (sorry, I'm not an expert in technical terms, like world or local rotations or the like), but try not to rush into marking it as a duplicate or closing it before you are absolutely sure about it.
Your question is pretty long, but I think what you're looking for is "rotation order". Rotations are typically applied to the x, then y, finally z axes.
You might want to put the earth's tilt (z) first, and daily spin (y) last, so you can change this with: globe.rotation.order = "ZXY"
See here for official documentation on rotation order

My camera does not move correctly through 3d space

I have created a raytracing algorithm which currently displays a triangle on screen, and am now having trouble moving the camera around correctly.
I would like my code to have the arrow keys rotate the camera, and WASD move the camera in 3D space. The camera currently rotates correctly using two rotation matrices for y and z rotations.
The Problem
Moving the camera, rather than rotating, is where the issue arises. To move the camera, I require two vectors - cameraForward and cameraRight. These vectors can be added on to the position of the camera when input is detected. These vectors will also need to change when the cemara is rotated, in the same rotation as all the rays experience. But when I apply these rotation matrices to the vectors representing cameraRight and cameraForward, there seems to be an issue. Holding down the A or D key will result in the camera moving unexpectedly in circles or odd wavy lines.
I managed to fix the issue with cameraForward by using a different method. I added a couple lines of code which calculate when the ray at the centre as been 'fired' and proceed to set cameraForward to that ray. Therefore cameraForward will always follow the central ray being sent out i.e. the centre of the field of view. However, I cannot do the same with cameraRight, as this vector is not in the field of view.
Before solving cameraForward using this method the same issue arose with moving forwards and backwards.
I also tried taking the cross product of one of the other rays along with the cameraForward vector which I thought might produce cameraRight, but to no avail - more sporadic camera movement
I do not have the vector for cameraUp either so cannot calculate the cross product to find cameraRight.
I also thought maybe the code was being run too many times and the vector was rotated multiple times. However mvoing the code elsewhere had no effect and the method it was already in is run every frame, so I do not believe that is the issue.
Here is my code to rotate the camera right and the method which does the rotation.
camRight's inital value is (0, 0, 1)
camRight = normalize(rotateVector(camRight, rotationZ));
camRight = normalize(rotateVector(camRight, rotationY));
function rotateVector(v, m) {
return new Vector3(dot(m.a, v), dot(m.b, v), dot(m.c, v));
}
I know this code works as the code rotating the camera view functions correctly using the same matrices and methods.
(the following code)
myDirection = normalize(rotateVector(myDirection, rotationZ));
myDirection = normalize(rotateVector(myDirection, rotationY));
When the user presses A or D the following code is run
if (keys[65]) {
camPos = add(camPos, normalize(camRight));
requestAnimationFrame(render);
}
if (keys[68]) {
camPos = subtract(camPos, normalize(camRight));
requestAnimationFrame(render);
}
The camera moves forwards and backward correctly, as previously mentioned. Initially, the camera moves left and right correctly too (as its inital value of (0, 0, 1) is correct), but if I rotate the camera, the values for cameraRight go wild.
Have I assumed something wrongly? or is there a flaw in my logic somewhere?
Thank for any help

Threejs - Translate object based on quaternion rotation

I have an object that is using a quaternion. This quaternion gets updated on a user's keypress to allow the object to rotate it's internal axis. The rotations are working fantastic. When in it's default state (before a user adjusts the rotation) my quaternion reads w:1, x:0, y:0, z:0
I want this object to always move forward in space via it's internal z-axis - THREE.Vector3(0,0,-1). How can I use the quat to know where it's z-axis is and move it forward?
For example - when my quaternion reads w:0.7, x:0.7, y:0, z:0 - the object is rotated to have it's z-axis pointed upward. Is there a way to move an object via it's internal axis?
Here's a jsfiddle example: http://bitly.com/RXPy5w - I want the square to always move forward in relation to it's z-axis
Any help would be greatly appreciated.
All you have to do is add the following line in your render loop:
airplane.translateZ( 1 );
Updated Fiddle: http://jsfiddle.net/Lc8gH/21/ Enjoy!
And by the way, you can't "translate a quaternion". Keep studying until you understand what this code is doing.
A quaternion here represents a rotation in three-space. You need to first specify what initial orientation you're rotating from. Make that a unit vector in the direction of your starting orientation.
Now, to travel in the direction of your current orientation, you can rotate the initial-orientation vector through the rotation described by your current quaternion. Now you have a unit vector pointing in the right direction.
Finally, of course, you can scale that unit vector to represent a motion by a non-unit distance, add it to your current position, and you're done.

How can I find a set of coordinates relative to a rotation?

How can I find a position relative to a point that has been rotated? The idea is, that I'm rotating an entity at a certain position, and need to be able to find a position relative to that entity's rotation, such that 10 units above the entity relative to its rotation is different from 10 units directly above the entity. Hopefully this diagram should give you an idea of what I mean:
Note: I'm doing this in the <canvas> tag with pure Javascript.
Also note: I'm only just finishing Algebra, and have done just a little trigonometry and some geometry, so please make your explanation relatively clear. Sin, cos, and tangent are only just beginning to make sense, and I had to look up the concept of radians myself the other day in order to work with <canvas>'s rotation function (thanks, Wikipedia!).
Also also note: I have tried looking this up myself (Google, SO, elsewhere), but not knowing the proper terms, I wasn't able to find anything. I'm sure this must be fairly simple, but it may as well be Greek to me.
If all you're looking for is a point that is d units from a point (x,y) at an angle θ, where the angle is taken relative to the point as its origin, in the clockwise direction, from the positive y-axis, the point is given by (x + d*cos(π/2-θ), y + d*sin(π/2-θ))
You can do it with vectors and matrices.Let's say that the direction to the 25,10 is forward vector of your cube.So you can apply the rotation matrix to this vector ,that is multiplying this vector by rotation matrix which is rotated amount of degrees you need.That will return you your relative position.Hope it is clear.I am not JavaScript expert.But I am sure there is some Vector math API out there you can use.
You have to do these steps:
Calculate the vector from cubes position to your desired offset before the rotation.
Calculate rotation matrix with some rotation.
Multiply that vector by the rotation matrix to get the new rotated vector.
I found this JavaScript Vector math lib.Take a look at the Matrix object.It has a method for vector multiplication.
If you kow how to rotate the square, you know howto rotate the vector.
What you did with the square (25,25), do with the vector(25,15)

Isometric Screen to Map

I'm trying to figure out how I can get the correct "active" tile under the mouse when I have "ramp" and +1 height tiles (see picture below).
When my world is flat, everything works no problem. Once I add a tile with a height of say +1, along with a ramp going back to +0, my screen -> map routine is still looking as if everything is "flat".
In the picture above, the green "ramp" is the real tile I want to render and calculate mouse -> map, however the blue tile you see "below" it is the area which gets calculated. So if you move your mouse into any of the dark green areas, it thinks you're on another tile.
Here is my map render (very simple)
canvas.width = canvas.width; // cheap clear in firefox 3.6, does not work in other browsers
for(i=0;i<map_y;i++){
for(j=0;j<map_x;j++){
var xpos = (i-j)*tile_h + current_x;
var ypos = (i+j)*tile_h/2+ current_y;
context.beginPath();
context.moveTo(xpos, ypos+(tile_h/2));
context.lineTo(xpos+(tile_w/2), ypos);
context.lineTo(xpos+(tile_w), ypos+(tile_h/2));
context.lineTo(xpos+(tile_w/2), ypos+(tile_h));
context.fill();
}
}
And here is my mouse -> map routine:
ymouse=( (2*(ev.pageY-canvas.offsetTop-current_y)-ev.pageX+canvas.offsetLeft+current_x)/2 );
xmouse=( ev.pageX+ymouse-current_x-(tile_w/2)-canvas.offsetLeft );
ymouse=Math.round(ymouse/tile_h);
xmouse=Math.round(xmouse/(tile_w/2));
current_tile=[xmouse,ymouse];
I have a feeling I'll have to start over and implement a world based map system rather than a simple screen -> map routine.
Thanks.
Your assumption is correct. In order to "pick" against world geometry, your routine needs to be aware of the world (and not just the base-level tile configuration). That is, without any concept of the height of the tiles near the one that is currently picked (by your current algorithm), there's no way to determine whether a neighboring tile (or one even further away, depending on the permitted height) should be intercepted by picking ray.
You've got the final possible point of your picking ray, already. What remains is to define the remainder of the ray, in world-space, and to check that ray for intersections with world geometry.
If, like the picture, your view angle is always 45 degrees and always from the same direction, your mouse -> map routine could use an algorithm something like:
calculate i,j of tile as you're doing currently (your final value of xmouse, ymouse)
look up height and angle of tile at i,j
given the height and angle, does this tile intersect the picking ray? If so, set lasti, lastj = i, j
increment/decrement i,j one step diagonally toward viewer
have we fallen off the edge of the map? If so, return lasti, lastj. Otherwise go back to 2.
Depending on the maximum height of a tile, you might have to check only 2 tiles, rather than going all the way to the edge of the map.
3 is the tricky part, and depends on your world geometry. Draw some triangles and you should be able to figure it out. Or you might try looking at the function intersect_quadrilateral_ray() here.

Categories

Resources