Ruben Mampaey
Ruben Mampaey

Reputation: 33

How can I rotate a Three.js group around a point inside the group?

tl;dr:The goal is to change the direction of the arrow around the circle.

We have a method that adds a shape (https://i.sstatic.net/l3lZB.jpg)(that consists of three Meshes) to the scene. Default it's drawn pointing to the right, but we want to give it a rotation (90°, Pi/2 pointing up, 180°, Pi pointing right and so on). The center point (the two circles) should always be at the same palce, only the triangle should rotate around the center point.

We tried working with Euler rotation and setting the axis tot the center point. Most of the time, rotating the node changes the position, but the x-y coordinates aren't changed in the group object.

So someone a solution on how we can rotate the node around the center? Below I've added the code which we use to add the node to the scene.

let scene;
let renderer;
let camera;

setup();

//params: xcoord, ycoord, rotation (in degrees)
drawNode(1, 1, 0);

renderer.render(scene, camera);

function setup() {
  scene = new THREE.Scene();

  renderer = new THREE.WebGLRenderer({
    alpha: true, //Tranpart
    antialias: true //For smoother edges
  });

  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  //we use ortographic camera so perspective doesn't manipulate how we see the objects
  //example: https://i.sstatic.net/q1SNB.png
  camera = new THREE.OrthographicCamera(
    window.innerWidth / -100, //Camera frustum left plane.
    window.innerWidth / 100, // Camera frustum right plane.
    window.innerHeight / 100, //Camera frustum top plane.   
    window.innerHeight / -100, //Camera frustum bottom plane
    -1, //near — Camera frustum near plane.
    100 //far — Camera frustum far plane.
    //frustum:https://en.wikipedia.org/wiki/Viewing_frustum
  );

  //Creates background grid 
  const gridHelper = new THREE.GridHelper(50, 50);
  gridHelper.rotateX(-Math.PI / 2); //Rotate is necessary because we changed the axis so Z is aimed to the screen
  scene.add(gridHelper);
}

function drawNode(xcoord, ycoord, rotation) {

  //Small white circle
  const innerCircleGeometry = new THREE.CircleGeometry(1 / 32, 32);
  const innerCircle = new THREE.Mesh(innerCircleGeometry, new THREE.MeshBasicMaterial({
    color: 0xFFFFFF
  }));

  innerCircle.position.set(xcoord, ycoord, 0);

  //Bigger grey circle
  const outerCircleGeometry = new THREE.CircleGeometry(1 / 16, 32);
  const outerCircle = new THREE.Mesh(outerCircleGeometry, new THREE.MeshBasicMaterial({
    color: 0xC4C4C4
  }));

  outerCircle.position.set(xcoord, ycoord, 0);

  //Points of the triangle
  const points = [];
  points.push(new THREE.Vector3(xcoord, ycoord - 1 / 4, 0));
  points.push(new THREE.Vector3(xcoord, ycoord + 1 / 4, 0));
  points.push(new THREE.Vector3(xcoord + 1 / 4, ycoord, 0));
  points.push(new THREE.Vector3(xcoord, ycoord - 1 / 4, 0));

  const geometrytriangle = new THREE.BufferGeometry().setFromPoints(points); //connects the points
  const triangle = new THREE.Mesh(geometrytriangle, new THREE.MeshBasicMaterial({
    side: THREE.DoubleSide,
    color: 0x5C5C5C
  }));

  //Set order so triangle is always on the bottom
  triangle.renderOrder = 0;
  outerCircle.renderOrder = 1;
  innerCircle.renderOrder = 2;

  const nodeGroup = new THREE.Group();
  nodeGroup.add(triangle);
  nodeGroup.add(outerCircle);
  nodeGroup.add(innerCircle);

  //TODO: rotate the node
  //Rotate that goes wrong: nodeGroup.rotateZ(THREE.MathUtils.degToRad(rotation));

  scene.add(nodeGroup);

  //Unique identifer of nodegroup, each child has own uuid.
  return nodeGroup.uuid;
}
body { min-height: 100vh; }
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
  <script src="https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js"></script>
  <title>Document</title>
</head>

<body>

</body>

</html>

Upvotes: 3

Views: 1619

Answers (1)

John Dood
John Dood

Reputation: 113

The trick here is that all rotations are about 0,0,0. In order to rotate the arrow, you need to center the spot you want to rotate about on the origin, rotate it, and then translate it.

function drawNode(xcoord, ycoord, rotation) {

  //Small white circle
  const innerCircleGeometry = new THREE.CircleGeometry(1 / 32, 32);
  const innerCircle = new THREE.Mesh(innerCircleGeometry, new THREE.MeshBasicMaterial({
    color: 0xFFFFFF
  }));

  innerCircle.position.set(0, 0, 0);

  //Bigger grey circle
  const outerCircleGeometry = new THREE.CircleGeometry(1 / 16, 32);
  const outerCircle = new THREE.Mesh(outerCircleGeometry, new THREE.MeshBasicMaterial({
    color: 0xC4C4C4
  }));

  outerCircle.position.set(0, 0, 0);

  //Points of the triangle
  const points = [];
  points.push(new THREE.Vector3(0, 0 - 1 / 4, 0));
  points.push(new THREE.Vector3(0, 0 + 1 / 4, 0));
  points.push(new THREE.Vector3(0 + 1 / 4, 0, 0));
  points.push(new THREE.Vector3(0, 0 - 1 / 4, 0));

  const geometrytriangle = new THREE.BufferGeometry().setFromPoints(points); //connects the points
  const triangle = new THREE.Mesh(geometrytriangle, new THREE.MeshBasicMaterial({
    side: THREE.DoubleSide,
    color: 0x5C5C5C
  }));

  //Set order so triangle is always on the bottom
  triangle.renderOrder = 0;
  outerCircle.renderOrder = 1;
  innerCircle.renderOrder = 2;

  const nodeGroup = new THREE.Group();
  nodeGroup.add(triangle);
  nodeGroup.add(outerCircle);
  nodeGroup.add(innerCircle);

  nodeGroup.rotateZ(rotation*3.14/180);
  nodeGroup.position.set(xcoord, ycoord, 0)

  scene.add(nodeGroup);

  //Unique identifer of nodegroup, each child has own uuid.
  return nodeGroup.uuid;
}

You can see my working example here

Upvotes: 2

Related Questions