What is the best way to rotate a group in ThreeJS while keeping its center as the pivot

I am currently attempting to construct a functioning Rubik's Cube using ThreeJS. However, I have encountered an issue with rotating the sides. To build the Rubik's Cube, I am adding individual smaller cubes in this manner:

const rubiksCube = new THREE.Group();
for (var i = 0; i < 27; i++) {
  // 3x3x3 (0,0,0) is located at the top left corner
  var cube = createPartOfCube(i, scene);
  cube.name = i;
  cube.position.x = (i % 3) * gap;
  cube.position.y = Math.floor(i / 9) * gap;
  cube.position.z = (Math.floor(i / 3) % 3) * gap;
  rubiksCube.add(cube);
}
scene.add(rubiksCube);

This setup is operating smoothly until I attempt to perform a rotation, such as the right side. When selecting the pieces on the right side and adding them to their own group, the resulting rotation occurs around the x-axis. Below is the method I am employing for moving a side:

function move(direction) {
    var bool = moves[direction];

    var pivot = new THREE.Group();
    pivot.updateMatrixWorld();

    for (var i = 0; i < 27; i++) {
      if (eval(format(bool, i))) {
        pivot.add(scene.getObjectByName(i));
      }
    }
    scene.add(pivot);
    animateMove(pivot);
}

For animating the movement:

function animateMove(pivot) {
    requestAnimationFrame(() => {
      animateMove(pivot);
    });
    // missing part
    renderer.render(scene, camera);
}

What I've Attempted:

I have tried various methods to achieve the correct rotation without success. Simply moving the cube is not providing the desired outcome.

One approach I explored was on this thread, but rotating in this manner caused the side to shift instead of rotating properly around the x-axis.

Minimal Reproducible Example

function main() {
  const scene = new THREE.Scene();
  const renderer = new THREE.WebGLRenderer();
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  const gap = 1.1;

  scene.add(new THREE.AxesHelper(100));
  scene.background = new THREE.Color('white');

  camera.position.z = 5;

  renderer.setSize(window.innerWidth, window.innerHeight);

  const rubiksCube = new THREE.Group();
  for (var i = 0; i < 27; i++) {
    // 3x3x3 (0,0,0) is located at the top left corner
    var cube = createPartOfCube(i, scene);
    cube.name = i;
    cube.position.x = (i % 3) * gap;
    cube.position.y = Math.floor(i / 9) * gap;
    cube.position.z = (Math.floor(i / 3) % 3) * gap;
    rubiksCube.add(cube);
  }

  scene.add(rubiksCube);
  animate();
}

function animate() {
  requestAnimationFrame(animate);
  const controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.target.set(1, 1, 1);
  controls.update();

  var movingIds = [2, 5, 8, 11, 14, 17, 20, 23, 26]; // IDs of the pieces needed to rotate
  var group = new THREE.Group();
  movingIds.forEach((i) => {
    group.add(scene.getObjectByName(i));
  });

  scene.add(group);
  animateMove(group);
}

function animateMove(group) {
  requestAnimationFrame(() => {
    animateMove(group);
  });
  group.rotation.x = 2; // Incorrect aspect needing assistance
  renderer.render(scene, camera);
}

function createPartOfCube() {
  var geometry = new THREE.BoxGeometry(1, 1, 1);
  var material = new THREE.MeshBasicMaterial({ color: 'black' });
  var cube = new THREE.Mesh(geometry, material);
  return cube;
}
main();

I hope that my dilemma is understood, and any help provided in resolving it would be greatly appreciated. Thank you!

Answer №1

When it comes to rotating objects, it's important to remember that all rotations occur around the object's origin point. For example, if your object is located at coordinates (0, 0, 0), then that will be the central pivot point for any rotations.

To maintain a consistent pivot point for all 27 pieces of your cube, I suggest nesting each piece within a THREE.Group(). This allows you to keep the pivot point fixed at (0, 0, 0) while positioning each individual piece accordingly:

const geom = new THREE.BoxGeometry(1, 1, 1);
const Piece = new THREE.Mesh(geom, mat);
const Group = new THREE.Group();

// Nest Piece inside its corresponding Group
Group.add(Piece);

// Set position of inner piece relative to parent Group
Piece.position.set(-1, -1, -1);

// By applying rotations to the parent Group,
// each child piece will maintain its distance from the center
Group.rotation.set(Math.PI / 2, 0, 0);

For each axis X, Y, and Z, make sure to repeat this process for values of [-1, 0, +1]. This results in a total of 3^3 = 27 unique pieces.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The pencil-drawn pixel on the canvas is positioned off-center

Currently, I am using p5.js to draw pixels on canvas with a pencil tool. However, I have encountered an issue where the pixel does not appear centered when the size of the pencil is set to 1 or smaller. It seems to be offset towards the left or right. Upo ...

Unable to delete items or clear selection

Looking for ways to remove a dynamically created duplicate select element using JavaScript. I have a scenario where there are two types of select elements. The first one is visible after clicking the "Add Element" button, containing options "Access Contro ...

Tips for effectively transferring information across nested ng-containers and ng-templates

I am in the process of developing a datatable module with a specific implementation in mind. My goal is to be able to import the module and utilize its components as shown below: randomcomponent.component.html <datatable [data]="tableData"> ...

Run a script on a specific div element exclusively

Up until this point, we have been using Iframe to load HTML and script in order to display the form to the user. Now, we are looking to transition from Iframe to DIV, but we are encountering an issue with the script. With Iframe, the loaded script is onl ...

Issue with Bootstrap alert persisting even after being displayed once

Having trouble with alert messages in jQuery where I display the message, validate data, and then try to hide it again. No errors in the console, even when logging before and after the process. SOLUTION $('#password, #confirm_password').on(& ...

Surprising block of text suddenly popped up on the browser screen while I was in the middle of working on

Currently delving into the world of MERN stack and working on a simple project. Everything was going smoothly on localhost until out of nowhere, some garbled text appeared on the screen, hindering my progress. I'm completely stumped as to what this my ...

As a result of the Chrome 53 bug, the Yahoo Weather API encountered an insecure certificate causing the jQuery AJAX request to fail

For the past year, I've relied on the Yahoo Weather API to provide me with weather updates. I've been utilizing the following link to access the data: https://query.yahooapis.com/v1/public/yql?q=select * from weather.forecast where woeid in (SELE ...

Display One Div at a Time in Sequence on Page Load Using jQuery

I have come across this question multiple times: How to Fade In images on page load using JavaScript one after the other? Fade in divs sequentially Using jQuery .fadeIn() on page load? Despite trying various recommended methods, I'm still unable t ...

Click event to verify, delete, and include class identifier in angular13

Looking to enhance functionality by dynamically adding and removing the 'active' class to 'li a' elements on click. While the current code performs well when clicking from top to bottom, it fails to work in reverse order. component.htm ...

Leverage Javascript to detect the operating system and dynamically incorporate external HTML content

I am trying to identify the operating system of a user and then dynamically load HTML content from another file based on their OS. I have incorporated jQuery scripts from the previous version of the site in my attempts, but they are not entirely effective. ...

Implementing dynamic title insertion into a popover element using jQuery

My goal is to assign a title to my popover object in a local project. I have already included the following files: bootstrap.css v4.2.1 jquery.min.js v2.2.0 bootstrap.min.js v4.2.1 popper.min.js v1.11.0 Initially, there was a basic button present. <i ...

What is the process for uploading a text file into JavaScript?

I'm currently facing an issue with importing a text file from my local computer into JavaScript in order to populate HTML dropdowns based on the content of the file. Despite spending considerable time searching for solutions on stack overflow, I haven ...

What is the method to disconnect clients using socket io?

I'm currently developing an application that allows clients to share real-time location data with one another using JavaScript. The issue I'm facing is that the data is being transmitted successfully on the server.js side, but it's not displ ...

Make sure to only update the state in useEffect after retrieving the data from the localStorage

Trying to troubleshoot the continuous looping and crashing issue in my app caused by passing variables in the dependency array of the useEffect hook. These variables are state variables from the context provider. The goal is to make the useEffect hook run ...

Ways to update a component from another in React

My React code includes an Employees page that renders both a Table component and a Filter component. The Filter component manipulates some data stored in the Employees page, updates the Filter's state through useEffect, and passes the data as a prop t ...

What is the best way to create a matrix using jagged arrays in Java?

Here is the code snippet I am working with: public class matrixExample { public static void main(String[] args) { int m[][] = new int[5][5]; int count = 1; for (int i=0; i<m.length; i++) for(int j=0; j< ...

Utilize React hooks to efficiently filter multiple JSON requests

I am currently working on creating a filter system that can combine multiple filters for users to choose from, such as "big/not-big" and "heavy/not-heavy". Each filter corresponds to loading a JSON file. My goal is to merge the results of these JSON files ...

Application crash imminent, alert: Uncaught TypeError detected - Unable to access property 'some' of undefined within React

My application has 3 sections. The first section consists of a form where users fill in details about watches, and the data is submitted using the submitHandler function. In the second part, users can enter watches from the first section. When a user click ...

Tips for sending AngularJS expressions to a controller

I am looking to pass a value from an AngularJS Expression to the controller. Here is the HTML code : <div data-ng-controller="AlbumCtrl"> <div data-ng-repeat="z in songInfo"> <div data-ng-repeat="b in z.album& ...

Error encountered while using Jest, React, Typescript, and React-Testing-Library

I have set up a project using React/NextJS with Typescript and now I am incorporating unit testing with Jest and React Testing Library. One of the unit tests for my component is as follows: import React from 'react'; import '@testing-libra ...