Rotate Object on Specific Axis Anywhere in Three.Js - Including Outside of Mesh

Rotate object on specific axis anywhere in Three.js - including outside of mesh

If you want to rotate an object around an arbitrary line in world space, you can use the following method. The line is specified by a 3D point and a direction vector (axis).

THREE.Object3D.prototype.rotateAroundWorldAxis = function() {

// rotate object around axis in world space (the axis passes through point)
// axis is assumed to be normalized
// assumes object does not have a rotated parent

var q = new THREE.Quaternion();

return function rotateAroundWorldAxis( point, axis, angle ) {

q.setFromAxisAngle( axis, angle );

this.applyQuaternion( q );

this.position.sub( point );
this.position.applyQuaternion( q );
this.position.add( point );

return this;

}

}();

three.js r.85

How do I rotate a mesh group about the axis of a random 3d object/mesh/3d line within the Three.js workspace?

So, yeah, this was a work in progress. And I've now gone far beyond that. For me this was a learning exercise in using Three.js for engineering related analysis. I'd like to share some tips I've learned.

  1. I found this posting with answer from "Stranger in the Q" The code is of a rotating Solar System with Sun, Earth (with revolving moon and rocks) and Mars (with two other planets.) I went through this example very carefully, understanding, really understanding all that was going on. The program made clear how rotations are propagated through to children (Moon rotates around the Earth, while the Earth rotates around the Sun, etc...) The content was managed by a simple object external to the ThreeJS scene. The program uses some recursive functions to manage actions by children elements in the animation functioning. There are some updates required, e.g. the constructor for THREE.Object3d no longer takes names. The corrective action was to simple add a name value after object creation. When you name everything accurately, its much easier to understand the entire child hierarchy. Again this example was instrumental in helping me to understand some of this stuff.

  2. The other thing I found was a posting with an answer from BadAskTechie The response was simple, and directly to the point.

    //rotates a mesh's geometry about a specified axis and pivot
    //the axis is a normalized Vector3
    const rotateAbout = (mesh, axis, axisPosition, angle) => {
    mesh.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(mesh.position.x-axisPosition.x, mesh.position.y-axisPosition.y, mesh.position.z-axisPosition.z)); //translate geometry to axis location
    mesh.geometry.applyMatrix(new THREE.Matrix4().makeRotationAxis(axis, angle)); //rotate geometry about axis
    mesh.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(axisPosition.x-mesh.position.x, axisPosition.y-mesh.position.y, axisPosition.z-mesh.position.z)); //translate geometry back to original location
    }
  3. One tip for complicated nested 3d meshes. Do all the geometry work, including component part orientation / translation / rotation, in the THREE.abcGeometry object stage. Don't create the meshs and then try to orient objects in the scene space. When you do the translations and rotations in geometry, everything lines up perfectly when it comes time for animation. Until I tried that nothing was making sense. The axis directions all seemed mixed up.

  4. There was one technique I found useful. I wanted to know where my elements were located in 3d space during rotation/animation functions. I could not figure out how to do that on 3d mesh objects. There are too many vertices to be useful. Instead I created a simple line object, and maneuver that along with the rest of the assembly during rotations. After an interim move, I could query the line by checking its two vertices, create a new 3d vector.normalize() and utilize that in future calculations. This seemed to work pretty well.

  5. The question I've asked above is really superseded by lots of progress on the whole skateboard truck model problem. Some of the code in the sandbox above is terrible. Please don't even waste time looking at it. Instead, I think what I'll do is open source the completed project, and list the github repo link and demo here, so others can get a glimpse on to my progress on same.

Perhaps that would be of use to others learning ThreeJS.

THREE Js Rotate Mesh on Y axis to face specific vector position

This is the THREE.Vector3 constructor (Docs):

Vector3( x, y, z )

x -- Float the vector's x value
y -- Float the vector's y value
z -- Float the vector's z value

You want to rotate a mesh on its Y axis, but you're actually rotating on its Z axis, so instead of:

var vector = new THREE.Vector3( 0, 0, 5);

try:

var vector = new THREE.Vector3( 0, 5, 0);

three.js: rotate object around world axis combined with tween.js

As I thought it was a really simple thing I had to do, to get it working:

I only needed to move the detachment of the child object (the dice) to the beginning of the function, instead of having it at the end of it and it works the charm.

Here's the working code:

// function for rotating dice
function moveCube() {
// detach dice from parent object first or attaching child object won't work as expected
THREE.SceneUtils.detach( dice, pivot, scene );
// reset parent object rotation
pivot.rotation.set( 0, 0, 0 );
pivot.updateMatrixWorld();
// attach dice to pivot object
THREE.SceneUtils.attach( dice, scene, pivot );

// set variables for rotation direction
var rotateZ = -1;
var rotateX = -1;
if (targetRotationX < 0) {
rotateZ = 1;
} else if (targetRotationY < 0) {
rotateX = 1;
}

// check what drag direction was higher
if (Math.abs(targetRotationX) > Math.abs(targetRotationY)) {
// rotation
var newPosRotate = {z: rotateZ * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
} else {
// rotation
var newPosRotate = {x: -rotateX * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
}
}

Thanks a lot for helping!

Rotating an Object properly around a pivot point given axis and angle

Question a)

Basically it works as expected:

    cylinder.position.set( options.x, 15, options.z );
pivot.position.x=options.x;
pivot.position.z=options.z;

see
https://jsfiddle.net/wf_bitplan_com/4f6ebs90/13/

Sample Image
Sample Image

Question b)

see
https://codepen.io/seppl2019/pen/zgJVKM

individually rotating arms

The key is to set the positions correctly. Instead of the proposal at https://stackoverflow.com/a/43837053/1497139 the size is computed in this case.

// create the pivot to rotate around/about
this.pivot = new THREE.Group();
this.pivot.add(this.mesh);
// shift the pivot position to fit my size + the size of the joint
this.pivot.position.set(
x,
y + this.size.y / 2 + this.pivotr,
z + this.size.z / 2
);
// reposition the mesh accordingly
this.mesh.position.set(0, this.size.y / 2, 0);

ThreeJS rotate around axis

The vector component of the Quaternion has to be (normalize.). The length of a normalized vector (Unit vector) is 1.0.

In your case the length of the vector component (THREE.Vector3(0.4, 0, 0.9)) is less than 1.0:

sqrt(0.9*0.9 + 0.0*0.0 + 0.4*0.4) = sqrt(0.81 + 0.16) = sqrt(0.97) = 0.9409

This causes that the cube scales sown by time. This can be verified by logging the scaling component (console.log(_initScale)).

If you would use a vector component with a length greater than 1.0 (e.g. THREE.Vector3(0.5, 0, 0.9), then the cube will scale up.

Normalize the axis of the Quaternion, to solve the issue:

let axis = new THREE.Vector3(0.4, 0, 0.9);
let q = new THREE.Quaternion().setFromAxisAngle(axis.normalize(), THREE.Math.degToRad(deg));
cube.matrix = rotateMatrix.compose(_initTranslation, q, _initScale);

If you want that one side of the cube is aligned to the axis, in that way, that the axis is normal to the side, then this is something completely different.

You've to do 2 rotations. First rotate the cube (e.g.) continuously around the x-axis, then turn the x-axis to the target axis (0.4, 0, 0.9). Use .setFromAxisAngle` to initialize a quaternion which rotates the x-axis to the target axis:

let x_axis = new THREE.Vector3(1, 0, 0);
let axis = new THREE.Vector3(0.4, 0, 0.9);
let q_align = new THREE.Quaternion().setFromUnitVectors(x_axis, axis.normalize());
let q_rotate = new THREE.Quaternion().setFromAxisAngle(x_axis, THREE.Math.degToRad(deg));
let q_final = q_align.clone().multiply(q_rotate);
cube.matrix = rotateMatrix.compose(_initTranslation, q, _initScale);

See the example, which compares the 2 different behavior:

var renderer, scene, camera, controls;var geometry, material, line, vertices, last, _initTranslation, _initRotation, initScale, rotateMatrix;
var deg = 0;
init();animate();
function init() {
document.body.style.cssText = 'margin: 0; overflow: hidden;' ;
renderer = new THREE.WebGLRenderer( { alpha: 1, antialias: true, clearColor: 0xffffff } ); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.set( 1, 3, 3 ); controls = new THREE.OrbitControls( camera, renderer.domElement ); geometry2 = new THREE.BoxGeometry( .5, .5, .5 ); material2 = new THREE.MeshNormalMaterial(); let shift = 0.5 cube = new THREE.Mesh( geometry2, material2 ); cube.matrix.makeTranslation(shift, 0, 0); scene.add( cube ); cube2 = new THREE.Mesh( geometry2, material2 ); cube2.matrix.makeTranslation(-shift, 0, 0); scene.add( cube2 ); material = new THREE.LineBasicMaterial({ color: 0x0077ff }); geometry = new THREE.Geometry(); geometry.vertices.push( new THREE.Vector3(-0.4, 0, -0.9), new THREE.Vector3(0.4, 0, 0.9) ); line = new THREE.Line( geometry, material ) line.position.set(shift, 0, 0); scene.add( line ); line2 = new THREE.Line( geometry, material ) line2.position.set(-shift, 0, 0); scene.add( line2 );
var sphereAxis = new THREE.AxesHelper(20); scene.add(sphereAxis); window.onresize = function() { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); }}
function animate() { rotate(deg) deg += 5 requestAnimationFrame( animate ); renderer.render(scene, camera); controls.update();}
function rotate(deg) { _initTranslation = new THREE.Vector3(); _initRotation = new THREE.Quaternion(); _initScale = new THREE.Vector3(); let x_axis = new THREE.Vector3(1, 0, 0); let axis = new THREE.Vector3(0.4, 0, 0.9);
// cube
cube.matrix.decompose(_initTranslation, _initRotation, _initScale); let q_align = new THREE.Quaternion().setFromUnitVectors(x_axis, axis.normalize()); let q_rotate = new THREE.Quaternion().setFromAxisAngle(x_axis, THREE.Math.degToRad(deg)); let q_final = q_align.clone().multiply(q_rotate); cube.matrix.compose(_initTranslation, q_final, _initScale); cube.matrixAutoUpdate = false; cube.matrixWorldNeedsUpdate = true;
// cube2
cube2.matrix.decompose(_initTranslation, _initRotation, _initScale); q = new THREE.Quaternion().setFromAxisAngle(axis.normalize(), THREE.Math.degToRad(deg)); cube2.matrix.compose(_initTranslation, q, _initScale); cube2.matrixAutoUpdate = false; cube2.matrixWorldNeedsUpdate = true;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script><script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Rotate a face along one of its edges

I was able to unfold a triangle to another triangle using the following snippet: Rotate object on specific axis anywhere in Three.js - including outside of mesh

    // Object contains only two faces
var geometry = object.children[0].geometry;

geometry.computeFaceNormals();
geometry.computeVertexNormals();

var f = geometry.faces[0];
var fprime = geometry.faces[1];

// Find two vertices in common
var edge = [f.a, f.b];
if (f.a != fprime.a && f.a != fprime.b && f.a != fprime.c) {
edge = [f.b, f.c];
} else if (f.b != fprime.a && f.b != fprime.b && f.b != fprime.c) {
edge = [f.a, f.c];
}

var point = geometry.vertices[edge[0]].clone();
var axis = geometry.vertices[edge[1]].clone();
axis = axis.sub(point);
axis.normalize();

// Adds a triangle to show rotation
var newGeometry = new THREE.Geometry();
newGeometry.vertices.push(
geometry.vertices[f.a].clone(),
geometry.vertices[f.b].clone(),
geometry.vertices[f.c].clone()
);
newGeometry.faces.push(new THREE.Face3(0, 1, 2));
var material = new THREE.MeshBasicMaterial({color: 0xffff00, side: THREE.DoubleSide});
var mesh = new THREE.Mesh(newGeometry, material);
scene.add(mesh);

var dot_product = f.normal.dot(fprime.normal); // Give cosinus of the angle
var angle = Math.acos(dot_product);

mesh.rotateAroundWorldAxis(point, axis, angle);

THREE.Object3D.prototype.rotateAroundWorldAxis = function() {

// rotate object around axis in world space (the axis passes through point)
// axis is assumed to be normalized
// assumes object does not have a rotated parent
var q = new THREE.Quaternion();

return function rotateAroundWorldAxis(point, axis, angle) {
q.setFromAxisAngle(axis, angle);
this.applyQuaternion(q);

this.position.sub(point);
this.position.applyQuaternion(q);
this.position.add(point);

return this;
}
}();

Result



Related Topics



Leave a reply



Submit