I have a bone in my scene, and want to rotate the bone so that it results in a different tail position, but by moving the tail only towards Z+ direction.
So, let’s say that there’s the bone head in this position.
And the original rotation for the bone makes the tail located in the certain position in the space.
I want to get the rotation that would put the tail in the new position (red) that keeps the angle and the original length?(I don’t know how to call it, but the red point in the XY plane is on the line that connects the (0,0) and the original (x,y).
This is my try so far, but it just moves the bone to some weird location. Any help would be appreciated!
function rotateBoneByDeltaZ(boneName: string, deltaZ: number) {
let children: Map<string, string> = new Map();
const targetBone = skeletonHelper.bones.find(bone => bone.name === boneName);
const childBone = skeletonHelper.bones.find(bone => bone.name === children.get(boneName));
const head = targetBone.position;
const tail = childBone.position;
let world_head = new THREE.Vector3();
targetBone.getWorldPosition(world_head);
let world_tail = new THREE.Vector3();
childBone.getWorldPosition(world_tail);
world_tail = new Vector3().subVectors(world_tail, world_head);
const new_world_tail = adjustZ(world_tail, deltaZ);
const targetAngle = calculateTargetAngle(world_tail, new_world_tail);
rotateBoneByGlobalAxis(boneName, targetAngle, 'Z');
}
function adjustZ(vector: Vector3, deltaZ: number): Vector3 {
const x = vector.x;
const y = vector.y;
const r = vector.length();
const theta = Math.atan2(y, x);
const z1 = vector.z + deltaZ;
if (z1 ** 2 > r ** 2) {
return vector;
}
const x1 = Math.sqrt(r ** 2 - z1 ** 2) * Math.cos(theta);
const y1 = Math.sqrt(r ** 2 - z1 ** 2) * Math.sin(theta);
// // print old size and new size
console.log(r, Math.sqrt(x1 ** 2 + y1 ** 2 + z1 ** 2));
return new Vector3(x1, y1, z1);
}
function calculateTargetAngle(tail: Vector3, newTail: Vector3): number {
const length = tail.length();
const v0 = tail.clone();
const v1 = newTail.clone();
const theta0 = Math.asin(v0.z / length);
const theta1 = Math.asin(v1.z / length);
return theta1 - theta0;
}
function rotateBoneByGlobalAxis(boneName: string, angle: number, axis: 'X' | 'Y' | 'Z') {
const targetBone = skeletonHelper.bones.find(bone => bone.name === boneName);
rotateAroundParallelAxis(targetBone, angle, axis);
}
function rotateAroundParallelAxis(object: Object3D, angle: number, axis: string) {
// Store the original position
const originalPosition = object.position.clone();
// Step 1: Translate the object such that the point coincides with the origin
object.position.sub(originalPosition);
// Step 2: Rotate the object around the global Z axis
switch (axis) {
case 'X':
object.rotation.x += angle;
break;
case 'Y':
object.rotation.y += angle;
break;
case 'Z':
object.rotation.z += angle;
break;
}
// Step 3: Translate the object back to its original position
object.position.add(originalPosition);
}