18

What I'm trying to achieve is a rotation of the geometry around pivot point and make that the new definition of the geometry. I do not want te keep editing the rotationZ but I want to have the current rotationZ to be the new rotationZ 0.

This way when I create a new rotation task, it will start from the new given pivot point and the newly given rad.

What I've tried, but then the rotation point moves:

// Add cube to do calculations
var box = new THREE.Box3().setFromObject( o );
var size = box.getSize();
var offsetZ = size.z / 2;

o.geometry.translate(0, -offsetZ, 0)

// Do ratation
o.rotateZ(CalcUtils.degreeToRad(degree));

o.geometry.translate(0, offsetZ, 0)

I also tried to add a Group and rotate that group and then remove the group. But I need to keep the rotation without all the extra objects. The code I created

var box = new THREE.Box3().setFromObject( o );
    var size = box.size();

    var geometry = new THREE.BoxGeometry( 20, 20, 20 );
    var material = new THREE.MeshBasicMaterial( { color: 0xcc0000 } );

    var cube = new THREE.Mesh( geometry, material );

    cube.position.x = o.position.x;
    cube.position.y = 0; // Height / 2
    cube.position.z = -size.z / 2;

    o.position.x = 0;
    o.position.y = 0;
    o.position.z = size.z / 2;

    cube.add(o);

    scene.add(cube);

    // Do ratation
    cube.rotateY(CalcUtils.degreeToRad(degree));

    // Remove cube, and go back to single object
    var position = o.getWorldPosition();

    scene.add(o)
    scene.remove(cube);
    console.log(o);

    o.position.x = position.x;
    o.position.y = position.y;
    o.position.z = position.z;

So my question, how do I save the current rotation as the new 0 rotation point. Make the rotation final

EDIT I added an image of what I want to do. The object is green. I have a 0 point of the world (black). I have a 0 point of the object (red). And I have rotation point (blue).

How can I rotate the object around the blue point?

enter image description here

Niels
  • 48,601
  • 4
  • 62
  • 81
  • Just to clarify: Are you trying to apply a rotation to an object, or to the vertices of the object? In other words, are you trying to persist the rotation by updating the vertices, rather than simply keeping a transformation matrix? – TheJim01 Mar 15 '17 at 14:56
  • Yes. Exaclty that. I want the rotation to be persisten. – Niels Mar 15 '17 at 14:57
  • 1
    @Niels You need to translate the geometry so that the desired rotation point maps to the world origin. Then apply the rotation. Then apply the inverse translation. – WestLangley Mar 18 '17 at 16:15
  • @WestLangley Thank you, I used TheJim1 answer to rotate, but used the local point as a value when I used the world as pointInWorld – Niels Mar 19 '17 at 09:49

2 Answers2

32

I wouldn't recommend updating the vertices, because you'll run into trouble with the normals (unless you keep them up-to-date, too). Basically, it's a lot of hassle to perform an action for which the transformation matrices were intended.

You came pretty close by translating, rotating, and un-translating, so you were on the right track. There are some built-in methods which can help make this super easy.

// obj - your object (THREE.Object3D or derived)
// point - the point of rotation (THREE.Vector3)
// axis - the axis of rotation (normalized THREE.Vector3)
// theta - radian value of rotation
// pointIsWorld - boolean indicating the point is in world coordinates (default = false)
function rotateAboutPoint(obj, point, axis, theta, pointIsWorld){
    pointIsWorld = (pointIsWorld === undefined)? false : pointIsWorld;

    if(pointIsWorld){
        obj.parent.localToWorld(obj.position); // compensate for world coordinate
    }

    obj.position.sub(point); // remove the offset
    obj.position.applyAxisAngle(axis, theta); // rotate the POSITION
    obj.position.add(point); // re-add the offset

    if(pointIsWorld){
        obj.parent.worldToLocal(obj.position); // undo world coordinates compensation
    }

    obj.rotateOnAxis(axis, theta); // rotate the OBJECT
}

After this method completes, the rotation/position IS persisted. The next time you call the method, it will transform the object from its current state to wherever your inputs define next.

Also note the compensation for using world coordinates. This allows you to use a point in either world coordinates or local space by converting the object's position vector into the correct coordinate system. It's probably best to use it this way any time your point and object are in different coordinate systems, though your observations may differ.

TheJim01
  • 8,411
  • 1
  • 30
  • 54
  • Thanks for the answer, almost got it to work, how do I get the normalized Vector3? What I currently do is: `rotateAboutPoint(mesh, new THREE.Vector3(0, 0, -355 / 2), new THREE.Vector3(0, 1, 0), THREE.Math.degToRad(30))` Currently it also moves the current position of the object. – Niels Mar 18 '17 at 14:21
  • Added a more detailed explaination in the question. – Niels Mar 18 '17 at 14:38
  • This also seems to rotate a little over another axis (z: attempting to rotate over y), do you have any idea what could be causing that? –  Aug 05 '19 at 17:19
  • @WarpDriveEnterprise No, sorry. If you're seeing strange behavior try to replicate it on your own. If everything looks right, post on the three.js discourse page to see if they think it's worth reporting as a bug. (I'm open to my code being wrong, but my example is highly generalized...) – TheJim01 Aug 05 '19 at 19:23
  • 4
    This should be included in Three.js in my opinion. – frankenapps Nov 14 '20 at 10:26
  • How can I make this not persistent? – Ood Nov 10 '21 at 14:11
  • I don't understand why you would rotate both the "position" *and* the "object". Isn't rotating the object the same thing as rotating its position? – bennlich Dec 23 '22 at 14:50
8

As a simple solution for anyone trying to quickly change the pivot point of an object, I would recommend creating a group and adding the mesh to the group, and rotating around that.

Full example

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)

enter image description here

Right now, this will just rotate around its center

cube.rotation.z = Math.PI / 4

enter image description here

Create a new group and add the cube

const group = new THREE.Group();
group.add(cube)
scene.add(group)

enter image description here

At this point we are back where we started. Now move the mesh:

cube.position.set(0.5,0.5,0)

enter image description here

Then move the group

group.position.set(-0.5, -0.5, 0)

enter image description here

Now use your group to rotate the object:

group.rotation.z = Math.PI / 4

enter image description here

Djave
  • 8,595
  • 8
  • 70
  • 124