How to link a specific position of one object to another

Hey everyone, I am trying to animate a rotation of 7 cubes that rotate about the z-axis. I figured out how to rotate it using the animation_time. However I want the cubes to remain connected to each other in a certain position(ie on their left corner). I know there’s a way to link/chain objects together, and I’ve read code about it. However the way that I have to code is lower level physically doing everything myself. So that being said here is how my cubes are “stored”,

let cubes = [];
for (let i = 0; i < 7; i++) {
    let cube = new THREE.Mesh(custom_cube_geometry, phong_material);
    cube.matrixAutoUpdate = false;
    cubes.push(cube);
    scene.add(cube);
}

and here is my animation implimentation:

let animation_time = 0;
let delta_animation_time;
let rotation_angle;
const clock = new THREE.Clock();
const MAX_ANGLE = 20 * Math.PI / 360; // 20 degrees converted to radians
const T = 3; // Oscillation period in seconds
function animate() {
    delta_animation_time = clock.getDelta();
    animation_time += delta_animation_time;

    // sinusoidal movement as a function of time
    rotation_angle = MAX_ANGLE * (Math.sin((2 * Math.PI / T) * animation_time) + 1);
    let animatedAngle = rotationMatrixZ(rotation_angle);
    
    for (let i = 0; i < cubes.length; i++) {
        if (i === 0) {
            // Apply initial transformation to the first cube
            model_transformation.identity(); // Reset to identity for the first cube
            model_transformation.multiplyMatrices(scaler, model_transformation); // Scale the first cube
            cubes[i].matrix.copy(model_transformation); // Copy the transformation to the first cube
        } else {
            // chain transformations for subsequent cubes
            model_transformation.multiplyMatrices(translation, model_transformation); // Translate by new scaled units
            //if i change this to animated angle then each cube rotates individually instead
            //model_transformation.multiplyMatrices(rotation, model_transformation); // Rotate by 20 degrees
            //model_transformation.multiplyMatrices(translationTest, model_transformation); // Adjust translation for corner touch
            //model_transformation.multiplyMatrices(scaler, model_transformation); // Scale height of the cube
            //this kinda works now it just rotates back to the corner which is better than before
            //model_transformation.multiplyMatrices(animatedAngle, model_transformation);
            model_transformation.multiplyMatrices(animatedAngle, model_transformation);
            cubes[i].matrix.copy(model_transformation); // Copy the chained transformation to each cube
        }

    }

    // Render the scene and update controls
    renderer.render(scene, camera);
    controls.update();
    // Continue the animation
    requestAnimationFrame(animate);
}

I want to ensure that the cubes remain connected to each other by one of their corners(ie the previous cubes top left corner and the next cubes bottom left corner). I know that this is what heircary is useful for/ what I need but I don’t know how to implement it using my specific situation which is an array. I drew a little visual of what I want to happen.

There is a perfect solution along with explanations on how it works, that you simply ignored in your thread from yesterday. You even chose to mark a comment that wasn’t a solution as the accepted solution, even though it didn’t help you solve your issue, obviously. That’s not cool.

The solution I mark did solve it when I statically rotated my cubes. Animating it is the solution that you are referring to however I don’t know how to implement it in my code because of my useage of an array, which is why I posted here. I tried to translate it by root of my size however, where I am implementing it in my for loop is where I believe I am going wrong.
Edit** I misspoke, I am not confused on the array, as Pavel’s solution utilizes a similar for loop, but because I can’t just say var geometry = new THREE.BoxGeometry( 1, 1, 1 ).translate(0.5,0.5,0). Because I physically am declaring my indices. Like so:

const custom_cube_geometry = new THREE.BufferGeometry();
custom_cube_geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
custom_cube_geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));
custom_cube_geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

I tried the following but it didn’t work:

const custom_cube_geometry = new THREE.BufferGeometry();
custom_cube_geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
custom_cube_geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));
custom_cube_geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));


const positionAttribute = custom_cube_geometry.getAttribute('position');
for (let i = 0; i < positionAttribute.count; i++) {
    positionAttribute.setXYZ(
        i,
        positionAttribute.getX(i) + 0.5,
        positionAttribute.getY(i) + 0.5,
        positionAttribute.getZ(i) + 0
    );
}

Since you generate your own box geometry, you can just make the lower left edge be at x=0, y=0, because you want to rotate the cube around the lower left edge, instead of the center of the box.

Or you can translate your geometry afterwards:
custom_cube_geometry.translate(.5, .5, 0) // assuming unit cubes here because you didn't specify

Check the working example and try to understand what’s going on. It’s really not that complicated.

Btw, 20 * Math.PI / 360; does not equal 20 degrees. Check your math again.

1 Like

Thanks for that, I was wondering why my rotation wasn’t nearly as deep as it should have been. When you refer to

custom_cube_geometry.translate(0.5, 0.5, 0);

Wouldn’t that just shift the entire block of cubes to a different location? Instead of shifting the geometry to move the pivot point from the center of the box to the lower left edge? Perhaps I am understanding where to place the translation incorrectly.

Answering the top question:

How to link a specific position of one object to another

You may use .localToWorld to get the global coordinates of a specific local point of an object. So, you may get the center of the upper left edge of a box and the center of the lower left edge of the upper box … and the differece between the two points is the amount of shift.

Here is another demo that:

  • does not nest cubes (they are independent objects)
  • they have random height
  • they move like a snake
  • the touching edge shifts depending on the angle
  • no manual matrix manipulation is done

The calculation is done in lines 69-75.

https://codepen.io/boytchev/full/jOgMGdM

image

My general advice is to avoid manual matrix manipulations unless you are confident you can control them well.

7 Likes

Okay using that logic I believe I understand it, and I am extremely close. However for some reason I have somehow attached the wrong corner?? Which doesn’t make sense to me because I am setting my matrix’s to be -l, -l, 0, and l, l, 0. Am I doing something glaringly incorrect which is what is messing me up??

const translation = translationMatrix(0, 3 * l, 0); //translate 3l units in the y-direction
const scaler = scalingMatrix(1, 1.5, 1); //scale height by 1.5
let model_transformation = new THREE.Matrix4(); // initalize transformation matrix for mesh cube
let model_transformation2 = new THREE.Matrix4(); // initalize transformation matrix for wireframe cube
const centerToTopLeft = translationMatrix(-l, -l, 0);
const topLeftToCenter = translationMatrix(l, l, 0);

And here’s my animate function:

function animate() {
    delta_animation_time = clock.getDelta();

    if (!still) {
        //only update the animation if still is false
        animation_time += delta_animation_time;
        rotation_angle = MAX_ANGLE * (Math.sin((2 * Math.PI / T) * animation_time) + 1);
    }

    let animatedAngle = rotationMatrixZ(rotation_angle);
    
    for (let i = 0; i < cubes.length; i++) {
        if (i === 0) {
            // Apply initial transformation to the first cube
            model_transformation.identity(); //reset to identity just in case
            model_transformation2.identity();
            //this worked
            // model_transformation, centerToTopLeft
            model_transformation.multiplyMatrices(centerToTopLeft, model_transformation);
            model_transformation2.multiplyMatrices(centerToTopLeft, model_transformation2);
            
            model_transformation.multiplyMatrices(scaler, model_transformation); //scale the first cube
            model_transformation2.multiplyMatrices(scaler, model_transformation2);
            //this worked but opposite corner
            // model_transformation, topLeftToCenter
            model_transformation.multiplyMatrices(topLeftToCenter, model_transformation);
            model_transformation2.multiplyMatrices(topLeftToCenter, model_transformation2);
            //
            cubes[i].matrix.copy(model_transformation); //copy the transformation to the first cube of mesh
            cubes_wireframe[i].matrix.copy(model_transformation2); //copy the transformation to the first cube of wireframe

        } else {
            //vertical translation upward
            model_transformation.multiplyMatrices(translation, model_transformation); 
            model_transformation2.multiplyMatrices(translation, model_transformation2); 
            //center to bottom left bottomLeftToCenter
            model_transformation.multiplyMatrices(centerToTopLeft, model_transformation);
            model_transformation2.multiplyMatrices(centerToTopLeft, model_transformation2);
            //animated rotation
            model_transformation.multiplyMatrices(animatedAngle, model_transformation);
            model_transformation2.multiplyMatrices(animatedAngle, model_transformation2);
            //bottom left to center 
            model_transformation.multiplyMatrices(topLeftToCenter, model_transformation);
            model_transformation2.multiplyMatrices(topLeftToCenter, model_transformation2);

            //copying model_transformation into the array's
            cubes[i].matrix.copy(model_transformation); 
            cubes_wireframe[i].matrix.copy(model_transformation2);   
        }
    }

    //render the scene and update controls
    renderer.render(scene, camera);
    controls.update();
    //continue the animation
    requestAnimationFrame(animate);
}

Edit I have figured out what I was doing wrong, my translations should have been from l, l, 0 to then -l, -l, 0. All of which before my vertical translation*, in order to pivot it about the bottom left corner instead of the top right corner which is what I had before.

1 Like